Notes on Lambda
By default, serverless framework uses Lambda-Proxy integration. Response must be: { statusCode: N, headers: { ... }, body: '....' } This is what you want 95% of time!!!
Lambda integration requires Method Request/Response <==> Integration Request/Response
Method Request specifies Authentication requirements. It exists even with proxy integration. The other 3 does not exist with proxy integration.
Integration Request/Response uses VTL templates. e.g. You can change it so that the query string is transformed to JSON :
#set($inputRoot = $input.path('$'))
{
"city": "$input.params('city')",
"time": "$input.params('time')",
}
The Lambda integration Response must be of the form::
{ somekey; someval, message : '.....' }
The input event is always a JSON object, all headers and query parameters already parsed for you :
{
"resource": "Resource path",
"path": "Path parameter",
"httpMethod": "Incoming request's method name"
"headers": {String containing incoming request headers}
"multiValueHeaders": {List of strings containing incoming request headers}
"queryStringParameters": {query string parameters }
"multiValueQueryStringParameters": {List of query string parameters}
"pathParameters": {path parameters}
"stageVariables": {Applicable stage variables}
"requestContext":
{Request context, including authorizer-returned key-value pairs}
"body": "A JSON string of the request payload."
"isBase64Encoded": "A boolean flag to indicate if the applicable
request payload is Base64-encode"
}
How to send non-json output ?
If the message contains "Error", you can map the error code accordingly.
If you are using proxy lambda integration your lambda should look like this:
// handler.js
// Response must be: { statusCode: N, headers: { ... }, body: '....' }
//
'use strict';
module.exports.publicHello = (event, context, callback) => {
// event is JSON representation of input.
const response = {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*', // Required for CORS support to work
},
body: JSON.stringify({
message: 'Public Hello!',
input: event,
}),
};
console.log('Public Event is', event)
console.log('Public Context is', context)
callback(null, response);
};
module.exports.privateHello = (event, context, callback) => {
const response = {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*', // Required for CORS support to work
},
body: JSON.stringify({
message: 'Private Hello!',
input: event,
}),
};
console.log('Private Event is', event)
console.log('Private Context is', context)
callback(null, response);
Example serverless framework template for lambda integration :
functions:
hello:
handler: handler.hello
events:
- http:
path: users
method: get
integration: lambda
request:
template:
text/xhtml: '{ "stage" : "$context.stage" }'
application/json: '{ "httpMethod" : "$context.httpMethod" }'
response:
headers:
Access-Control-Allow-Origin: "'*'"
statusCodes:
400:
pattern: '.*wrong.*'
template:
application/json: >
#set ($errorMessageObj = $input.path('$.errorMessage'))
$errorMessageObj
Example when using with ANY method:
exports.handler = async (event) => {
console.log(event);
if (event.httpMethod === 'PUT'){
let response = putMovie(event)
return done(response);
} else if (event.httpMethod === 'GET'){
let response = getMovie(event);
return done(response);
}
};
//
// e.g. return done({ message: 'OK!', somekey : someval })
//
const done = response => {
return {
statusCode: '200',
body: JSON.stringify(response),
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Methods': '*',
'Access-Control-Allow-Origin': '*'
}
}
}
const AWS = require('aws-sdk')
const Lambda = new AWS.Lambda()
async function invokeLambda(functionName) {
const req = {
FunctionName: functionName,
Payload: JSON.stringify({ message: 'hello world' })
}
await Lambda.invoke(req).promise()
}
A cold start happens when code is executed for the first time and consists of three stages:
During throttling Lambda returns the following to API Gateway:
{
"Reason" :"ReservedFunctionConcurrentInvocationLimitExceeded",
"Type" :"User",
"message":"Rate Exceeded."
}
For API Gateway proxy integration, this is wrong format. It expects response in this format :
{
statusCode: '200',
body: JSON.stringify(response, null, 2),
headers: { ... }
}
Each service may have its own retry policy. API Gateway does not try to retry the failed calls.
//
// Prefer this when you do not have promise on hand.
// As soon as he callback is called, the main job is done.
//
exports.handler = (event, context, callback) => {
var params = {
Bucket: "examplebucket",
Key: "HappyFace.jpg"
};
s3.getObject(params, function(err, data) {
if (err) return callback(err);
callback(null, data);
});
}
//
// Prefer this following style in general whenever possible.
//
exports.handler = async event => {
var params = {
Bucket: "examplebucket",
Key: "HappyFace.jpg"
};
return await s3.getObject(params).promise();
}