lambda-local icon indicating copy to clipboard operation
lambda-local copied to clipboard

Lambda together with API Gateway

Open Mojo90 opened this issue 8 years ago • 7 comments

I use a stack where API Gateway invokes Lambda Function via aws_proxy and this works great. The docs of aws says Requests will be proxied to Lambda with request details available in the "context" of your handler function. So I am wondering if I can invoke it the same way local with this plugin? I did a few tests and generally the invocation works good but didn't get it to run as I needed to. e.g. apigateway gets a get request to path hello and this invokes the lambda function with a proxy and you get a corresponding answer (normal rest api).

Mojo90 avatar Feb 10 '17 22:02 Mojo90

Hi! So I believe you are asking for some request details to be set inside context? If that is the case, I totally agree. But I would want to see the official specifications of when&what information is set into context.

As far as I have researched:

  1. the request details from API Gateway are set inside the event, not context http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-set-up-simple-proxy.html

With the Lambda proxy integration, API Gateway maps the entire client request to the input event parameter of the back-end Lambda function as follows:

  1. The official documentation of the context object does not mention anything about request details from API Gateway http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html

I'd appreciate it if you can show me where you found the documentation. Thanks!

ashiina avatar Feb 11 '17 01:02 ashiina

To be honest I searched for a while now and it was not from the docs it was from tooltips in API Gateway: screen shot 2017-02-11 at 16 11 51

So I think you already pointed to the correct URL to this Section: Input Format of a Lambda Function for Proxy Integration http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-set-up-simple-proxy.html#api-gateway-simple-proxy-for-lambda-input-format

I tried it with an event based on above url but I get a 500 error with [Error: "value" required in setHeader("x-apigateway-context", value).]

Mojo90 avatar Feb 11 '17 15:02 Mojo90

What is stopping you from adding the full content of the API gateway request to your sample input data eg:

module.exports = {
    resource: '/{proxy+}',
    path: '/proxy',
    httpMethod: 'POST',
    headers: {
        Accept: '*/*',
        'Accept-Encoding': 'deflate, gzip',
        'CloudFront-Forwarded-Proto': 'https',
        'CloudFront-Is-Desktop-Viewer': 'true',
        'CloudFront-Is-Mobile-Viewer': 'false',
        'CloudFront-Is-SmartTV-Viewer': 'false',
        'CloudFront-Is-Tablet-Viewer': 'false',
        'CloudFront-Viewer-Country': 'US',
        'Content-Type': 'application/json',
        Host: 'xxx.execute-api.ap-southeast-2.amazonaws.com',
        Via: '1.1 xxx.cloudfront.net (CloudFront)',
        'X-Amz-Cf-Id': 'xxx',
        'X-Forwarded-For': '69.63.188.127, 54.239.134.6',
        'X-Forwarded-Port': '443',
        'X-Forwarded-Proto': 'https',
        'X-Hub-Signature': 'sha1=xxx'
    },
    queryStringParameters: null,
    pathParameters: {
        proxy: 'proxy'
    },
    stageVariables: null,
    requestContext: {
        accountId: 'xxx',
        resourceId: '10999l',
        stage: 'Prod',
        requestId: '23dcdd78-eebf-11e6-97f8-25bc6590f380',
        identity: {
            cognitoIdentityPoolId: null,
            accountId: null,
            cognitoIdentityId: null,
            caller: null,
            apiKey: null,
            sourceIp: '69.63.188.127',
            accessKey: null,
            cognitoAuthenticationType: null,
            cognitoAuthenticationProvider: null,
            userArn: null,
            userAgent: null,
            user: null
        },
        resourcePath: '/{proxy+}',
        httpMethod: 'POST',
        apiId: 'jq8asr1y7f'
    },
    body: '{"object":"page","entry":[{"id":"xxx","time":1486641538051,"messaging":[{"sender":{"id":"xxx"},"recipient":{"id":"xxx"},"timestamp":1486596726994,"message":{"mid":"mid.1486596726994:606c1bfc42","seq":221055,"text":"test"}}]}]}',
    isBase64Encoded: false
}

mitchell-johnson avatar Feb 12 '17 08:02 mitchell-johnson

@mitchell-johnson As I mentioned, I tried with such an event but got a 500-Error and the comment: [Error: "value" required in setHeader("x-apigateway-context", value).]

Mojo90 avatar Feb 12 '17 16:02 Mojo90

@Mojo90 I just tested this myself, and it turns out that as the Document states, all the request information is stored in the event of the handler. Therefore, as @mitchell-johnson states, you should be able to pass it into lambda-local as an input data. You should also try and look at all of the event and context data, and you should be able to verify this yourself.

The text on the console (the screenshot you pasted) turns out to be wrong... A silly mistake on AWS's part.

http://willhamill.com/2016/12/12/aws-api-gateway-lambda-proxy-request-and-response-objects

ashiina avatar Feb 16 '17 02:02 ashiina

[Error: "value" required in setHeader("x-apigateway-context", value).]

This sounds like a completely different issue. If you want help with this you should post your actual Lambda code.

ashiina avatar Feb 16 '17 02:02 ashiina

I am currently using swagger-node with aws-serverless-express. To reproduce this error:

If you don't have used swagger before install it with:

$ npm install -g swagger

create swagger-node project (use express):

$ swagger project create hello-world
$ cd hello-world/
$ npm i --save aws-serverless-express

update app.js:

'use strict';

var SwaggerExpress = require('swagger-express-mw');
var app = require('express')();
module.exports = app; // for testing

var awsServerlessExpressMiddleware = require('aws-serverless-express/middleware');

app.use(awsServerlessExpressMiddleware.eventContext());

var config = {
  appRoot: __dirname // required config
};

SwaggerExpress.create(config, function(err, swaggerExpress) {
  if (err) { throw err; }

  // install middleware
  swaggerExpress.register(app);

  var port = process.env.PORT || 10010;
  app.listen(port);

  if (swaggerExpress.runner.swagger.paths['/hello']) {
    console.log('try this:\ncurl http://127.0.0.1:' + port + '/hello?name=Scott');
  }
});

add lambda.js:

const awsServerlessExpress = require('aws-serverless-express');
const app = require('./app');
const server = awsServerlessExpress.createServer(app);

exports.handler = (event, context) => awsServerlessExpress.proxy(server, event, context);

add test-event.js:

module.exports = module.exports = {
    resource: '/{proxy+}',
    path: '/proxy',
    httpMethod: 'POST',
    headers: {
        Accept: '*/*',
        'Accept-Encoding': 'deflate, gzip',
        'CloudFront-Forwarded-Proto': 'https',
        'CloudFront-Is-Desktop-Viewer': 'true',
        'CloudFront-Is-Mobile-Viewer': 'false',
        'CloudFront-Is-SmartTV-Viewer': 'false',
        'CloudFront-Is-Tablet-Viewer': 'false',
        'CloudFront-Viewer-Country': 'US',
        'Content-Type': 'application/json',
        Host: 'xxx.execute-api.ap-southeast-2.amazonaws.com',
        Via: '1.1 xxx.cloudfront.net (CloudFront)',
        'X-Amz-Cf-Id': 'xxx',
        'X-Forwarded-For': '69.63.188.127, 54.239.134.6',
        'X-Forwarded-Port': '443',
        'X-Forwarded-Proto': 'https',
        'X-Hub-Signature': 'sha1=xxx'
    },
    queryStringParameters: null,
    pathParameters: {
        proxy: 'proxy'
    },
    stageVariables: null,
    requestContext: {
        accountId: 'xxx',
        resourceId: '10999l',
        stage: 'Prod',
        requestId: '23dcdd78-eebf-11e6-97f8-25bc6590f380',
        identity: {
            cognitoIdentityPoolId: null,
            accountId: null,
            cognitoIdentityId: null,
            caller: null,
            apiKey: null,
            sourceIp: '69.63.188.127',
            accessKey: null,
            cognitoAuthenticationType: null,
            cognitoAuthenticationProvider: null,
            userArn: null,
            userAgent: null,
            user: null
        },
        resourcePath: '/{proxy+}',
        httpMethod: 'POST',
        apiId: 'jq8asr1y7f'
    },
    body: '{"object":"page","entry":[{"id":"xxx","time":1486641538051,"messaging":[{"sender":{"id":"xxx"},"recipient":{"id":"xxx"},"timestamp":1486596726994,"message":{"mid":"mid.1486596726994:606c1bfc42","seq":221055,"text":"test"}}]}]}',
    isBase64Encoded: false
};

now run:

$ lambda-local -l lambda.js -e test-event.js

output:

info: Logs
info: ------
info: START RequestId: 7fe40093-8ca7-22f3-6c7f-b8ade54aac7d
EADDRINUSE /tmp/server0.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server1.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server2.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server3.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server4.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server5.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server6.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server7.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server8.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server9.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server10.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server11.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server12.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server13.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server14.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server15.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server16.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server17.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server18.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server19.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server20.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server21.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server22.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server23.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server24.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server25.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server26.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server27.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server28.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server29.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server30.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server31.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server32.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server33.sock incrementing socketPathSuffix.
EADDRINUSE /tmp/server34.sock incrementing socketPathSuffix.
[Error: `value` required in setHeader("x-apigateway-context", value).]
info: END
info: Message
info: ------
info: {
	"statusCode": 500,
	"body": "",
	"headers": {}
}
info: -----
info: lambda-local successfully complete.

so does not work for me. At least a 404 should occur, as a 404 occurs when executing lambda function in aws console and no event data with proxy information is setted. Thx for your time!

Mojo90 avatar Feb 16 '17 12:02 Mojo90