I have a serverless lambda function written in Node.JS.
What is the best / correct way of returning error codes?
The pattern that I use right now (and it works!) is:
module.exports.endpoint = (event, context, callback) => {
const response = {
statusCode: 404,
body: JSON.stringify({ message: 'Hello World!' })
};
callback(null, response);
}
When I make a call, for example from POSTMAN, to my endpoint I get:
Status: 404 Not Found
which is exactly what I'm expecting.
Moreover, in the logs I can see:
Serverless: GET / (λ: get)
Serverless: [404] {"statusCode":404,"body":"{\"message\":\"Hello World!\"}"}
That works well.
What bothers me is that I'm passing null
as the error. Looking into a few other tutorials/examples I've found patterns like:
https://aws.amazon.com/blogs/compute/error-handling-patterns-in-amazon-api-gateway-and-aws-lambda/
https://serverless.com/framework/docs/providers/aws/events/apigateway/
callback ("the sky is falling!");
callback("[BadRequest] Validation error: Missing field 'name'");
callback("[404] Not Found");
callback(new Error('[404] Not found'));
callback(JSON.stringify(myErrorObj));
All of them make perfect sense and you can specify HTTP Status Code - yet what I'm getting is HTTP Status Code 200 in the end. When I look at the logs I can see that the error was followed straight after with 200:
Serverless: GET / (λ: get)
Serverless: Failure: the sky is falling!
Serverless: Replying 200
Serverless: GET / (λ: get)
Serverless: Failure: [BadRequest] Validation error: Missing field 'name'
Serverless: Replying 200
Serverless: GET / (λ: get)
Serverless: Failure: [404] Not Found
Serverless: Replying 200
Serverless: GET / (λ: get)
Serverless: Failure: [404] Not found
Serverless: Replying 200
Serverless: GET / (λ: get)
Serverless: Failure: {"errorType":"InternalServerError","httpStatus":500,"message":"An unknown error has occurred. Please try again."}
Serverless: Replying 200
In this place I've found following explanation: https://github.com/serverless/serverless/issues/4119
If you want to respond with HTTP errors in that case, you have to encode the HTTP error as successful Lambda response
with following example:
Sample 403:
callback(null, { statusCode: 403, body: "Forbidden", headers: { "Content-Type": "text/plain" } });
Sample 404:
callback(null, { statusCode: 400 });
So that's basically the same way I've. For the sake of completeness, I can add that there is also a plenty of examples that uses context.fail(result)
or context.succeed(result)
- but from what I gathered context
is deprecated and shouldn't be used (even though it still works).
What is the point of using callback(error)
?
For API Gateway to pass the error type (for example, InvalidParameterException ), as part of the response to the client, the Lambda function must include a header (for example, "X-Amzn-ErrorType":"InvalidParameterException" ) in the headers property.
The easiest way to set custom HTTP status code is to setup a Lambda Proxy Integration in API Gateway. In API Gateway > Resource > Actions Dropdown > Create Method > tick Lambda Proxy Integration and select appropriate Lambda function. For async functions just return with an object with statusCode and body .
If you want to respond with HTTP errors in that case, you have to encode the HTTP error as successful Lambda response
This type of error-handling is specific to API Gateway.
Just like in traditional Node web servers (e.g. express), you can throw any error using throw new Error('Invalid Payload')
and the middleware usually converts it into an HTTP response with the correct response status.
In API Gateway Lambda, this can be written like this...
function createResponse(status, body) {
return {
headers: {
'Access-Control-Allow-Origin': '*'
},
statusCode: status,
body: JSON.stringify(body)
}
}
module.exports.endpoint = (event, context, callback) => {
try {
return callback(null, createResponse(200, processEvent(event)))
} except (e)
console.error(e)
return callback(null, createResponse(500, {
error: 'Internal Server Error'
}))
}
Basically, it's a handled error. The lambda function succeeded but the request failed (maybe 400, 404, or 500).
You should be handling errors always, or else if your handler crashes (due to a runtime error or syntax error or any unhandled error), your user will get an unexpected response (a 500 or a 502) which you probably don't want.
Please remember that Lambda is not only used for API Gateway. callback(error)
is used for non-API Gateway-triggered Lambdas.
For example, if you have an SNS-triggered Lambda, you can return callback('Any error message here')
and it will let SNS know that it failed and so SNS can retry the invocation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With