TL; DR
- Your Lambda handler should throw an exception if you want to respond with non-200.
- Catch all exceptions in the handler method. Format the caught exception message in JSON and enter Exception as a custom type.
- Use the Integration Response response to regex your custom Exception found in the errorMessage field of the Lambda response.
API Gateway API + AWS Lambda Exception Handling
There are many things you need to know about Lambda, the Gateway API, and how they work together.
Lambda exceptions
When an exception is thrown from the handler / function / method, the exception is serialized into a JSON message. From your sample code, on 404 with S3, your code will throw:
{ "stackTrace": [ [ "/var/task/mycode.py", 118, "my_handler", "raise ClientException(\"Key '{}' not found \".format(filename))" ] ], "errorType": "ClientException", "errorMessage": "Key 'my_filename' not found" }
API Gateway Integration Response
Overview
Integration Responses maps Lambda responses to HTTP codes. They also allow you to change the body of the message as they pass.
By default, the โ200โ Integration Response response is configured for you, which passes all the responses from Lambda back to the client as it is, including serialized JSON exceptions, as an HTTP 200 (OK) response.
For good messages, you can use the โ200โ Integration Response response to map the JSON payload to one of your specific models.
Exception exceptions
For exceptions, you want to set the appropriate HTTP status code and possibly remove the stack to hide the insides of your code.
For each HTTP status code that you want to return, you need to add an "Integration Response" entry. The integration response is configured to match the regular expression (using java.util.regex.Matcher.matches() not .find() ), which corresponds to the errorMessage field. After the match is completed, you can customize the body mapping template to selectively format the appropriate exception body.
Since the regular expression matches only the body of errorMessage from the exception, you need to make sure that your exception contains enough information to allow the various integration answers to match and set the corresponding error. (You cannot use .* To match all exceptions, as this seems to match all answers, including non-exceptions!)
Value exceptions
To create exceptions with sufficient detail in your post, the error-handling-patterns-in-amazon-api-gateway-and-aws-lambda blog recommends creating an exception handler in your handler to populate the exception information in the JSON string that will be used in the exception message.
My preferred approach is to create a new top method as a handler that handles the response to the Gateway API. This method returns the required payload or throws an exception with the original exception encoded as a JSON string as an exception message.
def my_handler_core(event, context): try: s3conn.head_object(Bucket='my_bucket', Key='my_filename') ... return something except botocore.exceptions.ClientError as e: if e.response['Error']['Code'] == "404": raise ClientException("Key '{}' not found".format(filename)) def my_handler(event=None, context=None): try: token = my_handler_core(event, context) response = { "response": token }
In case of an exception, Lambda now returns:
{ "stackTrace": [ [ "/var/task/mycode.py", 42, "my_handler", "raise LambdaException(api_exception_json)" ] ], "errorType": "LambdaException", "errorMessage": "{\"message\": \"Key 'my_filename' not found\", \"type\": \"ClientException\", \"isError\": true}" }
Display errors
Now that you have all the details in errorMessage , you can start matching status codes and creating well-formed error messages. The Gateway API parses and cancels the errorMessage field, so the regular expression used does not need to be fixed.
example
To catch this ClientException as a 400 error and map the payload to a clean error model, you can do the following:
Create a new error model:
{ "type": "object", "title": "MyErrorModel", "properties": { "isError": { "type": "boolean" }, "message": { "type": "string" }, "type": { "type": "string" } }, "required": [ "token", "isError", "type" ] }
- Edit the โMethod Responseโ and map the new model to
400 - Add New Integration Response
- Set code
400 - Set a regular expression to match ClientException types with a space tolerance:.
.*"type"\s*:\s*"ClientException".* Add a body mapping template for application/json to map the contents of errorMessage to your model:
#set($inputRoot = $input.path('$')) #set ($errorMessageObj = $util.parseJson($input.path('$.errorMessage'))) { "isError" : true, "message" : "$errorMessageObj.message", "type": "$errorMessageObj.type" }