Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the correct way of returning HTTP error codes in serverless lambda

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)?

like image 528
iaforek Avatar asked Oct 11 '17 13:10

iaforek


People also ask

How do I send error response in Lambda?

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.

How do I return custom HTTP status codes from a Lambda function in Amazon API gateway?

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 .


1 Answers

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.

like image 175
Noel Llevares Avatar answered Oct 04 '22 19:10

Noel Llevares