Disclaimer: This article cover Lambda custom integration only, Lambda proxy integration will work differently
It's a very common strategy to create your function in AWS Lambda and create REST API on API Gateway and integrate them together, however, there's one common and simple mistake that many developer overlook when doing so, especially if you move from local development to Lambda. Let's see the example below
exports.handler = async (event) => {
// TODO implement
const response = {
statusCode: 400, // Change from 200 to 400
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
The above code blocks is a little modification from pre created code when choose "Author from scratch" when creating Lambda with NodeJS. The only change is from statusCode 200 to 400. Then create a new REST API method point to this lambda function... deploy and done. This is how simple it look like to create REST API, When looking at the code, many developer will say that this will return HTTP Status 400 right? but it's not. You will get response 200 with data statusCode: 400
. This case can easily interpret that it's because we return content not HTTP Status.
(For Lambda proxy integration this function will response correctly with HTTP 400 Bad Request)
( For more information about HTTP Status Read Here )
Let's try another example which is more obvious, this time intentionally throw Error in Lambda.
exports.handler = async (event) => {
// TODO implement
throw new Error('Internal Error - Intentionally throw this error');
};
Try calling it and get HTTP 200 again with error message as content.
(For Lambda proxy integration this function will response correctly with HTTP 502 Bad Gateway)
Look like It's Okay to Not Be Okay
(Image from Kdramalove.com - Yes... I like Korean series.. and Seo Ye-ji is gorgeous)
If you have experience throwing error and get HTTP 500 when developing locally with express or any framework. That happen because framework handle that error for you and generate correct HTTP Status, while Lambda and API Gateway is totally different services and what API Gateway looking for, by default, is that Lambda answer something and HTTP 200 you get mean yes, your code was executed.
Setup Method Response in API Gateway
First we need to define which HTTP Status we want to send back to client. We can do this in Method Response in API Gateway. So let's add the following error
- HTTP 500 (Internal Server Error) for error that has been generated when we call
throw Error()
(Second case above). According to AWS Documentation, this type of error call Standard Lambda Error - HTTP 400 (Bad Request) for error that we intentionally response with specific formatted string (First case above). According to AWS Documentation, this type of error call Custom Lambda Error
Setup Integration Response in API Gateway
Next, move to Integration Response to configure each case. The general idea for configuring this is to set a pattern (regular expression) that will be tested against response from Lambda function, if the pattern matched, we will get HTTP Response governing that pattern. If no patterns matched, we will get the default HTTP response. (configured to 200 by default or customize by configured with pattern .*
)
Furthermore, we can setup response body using Mapping Template.
Handle Standard Lambda Error (500)
For this type of error, when we call throw Error()
, Lambda will automatically generate response as shown in screenshot above. In order to test pattern against this type of error, API Gateway will test configured pattern against errorMessage
field instead of whole error object.
In our case, we send our error message when generating this error by adding prefix 'Internal Error' in front of any detail. Let's setup using regular expression ^Internal Error.*
which will match whole errorMessage. According to my test, if we use partial match (e.g. Internal*) it will not work and we will still get HTTP 200 instead of 500.
Testing on postman again, now we will get HTTP 500 (Internal Server Error) with the same previous content because we use Passthrough for content handling above.
Now that we got correct error, but it's strange to stack trace to client. Let's set Mapping Template for application/json
type and return only errorMessage to client.
(There's a strange UX behavior here, when editing Mapping Template you have to grey Save down below text editor instead of clicking blue Save button above, which is more attractive to click.)
Deploy and test it to get the response we want!
Handle Custom Lambda Error (400)
For this case where we want to create our own response with error message and we know that this response is gonna be an error. The easiest way is to forcefully make this function failed and return and error using context.fail
instead of return
. Additionally, in order for API Gateway to parse error message, we need to convert JSON response to text and the code will be change to the following:-
exports.handler = async (event, context) => {
// TODO implement
const response = {
statusCode: 400, // Change from 200 to 400
body: JSON.stringify('Hello from Lambda!'),
};
context.fail(JSON.stringify(response))
};
Now that we cause error to occurred, Lambda will return and error object similar to standard error in previous case and we can use regex .*"statusCode":400.*
to detect which statusCode we send and return correct HTTP Response.
After testing, you will get HTTP Status 400 (Bad Request) with generated Lambda error object
Last step is to setup Mapping Template to return our intended object
Finally, we will get our same old object but with HTTP Status 400 instead of 200 as intended.
Conclusion
There's a lot more to explore and configure when working with AWS service, this one is just an example of a common mistake mostly happen when start using Lambda and API Gateway or when in need of a quick api creation. Anyway, I personally prefer using Lambda proxy integration which let me have more control. I hope this article help anyway to happen to pass through here or looking for this problem.
Lastly, I would be very appreciate for any comment, knowledge sharing and feedback. Happy coding!
Top comments (0)