I have class BadRequest(Exception): pass
in my Lambda function.
I'd like to raise BadRequest("Invalid request params")
and have the API return a response with status code 400 and body { "message": "Invalid request params" }
(or equivalent).
Simply doing that however returns a response with status code 200 (oh no!) and body
{
"errorMessage": "Invalid request params",
"errorType": "BadRequest",
"stackTrace": <my application code that the user shouldnt see>
}
After searching around online, it seems I have 3 options:
1) chalice
2) Use integration response and method response to parse that error into a better response. I would regex like [BadRequest].*
and insert a prefix when I throw the exception (not very elegant IMO).
3) Use Step Functions to create a stateful representation of the API. This seems a little tedious because I would need to learn ASL and I don't know any deaf people. -.-
Which rabbit hole should I go down and why?
I am returning to this question 3 years later to describe how I solve this problem today. I use the Serverless Framework to deploy my lambda functions and an API gateway.
I use a decorator that catches exceptions and returns a payload. For example, here is a successful request, an expected exception, and an unexpected exception.
def my_successful_request(event, context):
return {
"statusCode": 200,
"body": json.dumps({"success": True})
}
def handle_exceptions(f):
def deco(*args, **kwargs):
try:
return f(*args, **kwargs)
except BadRequest as e:
print(e)
return {"statusCode": 400, "body": json.dumps({"message": str(e)})}
except Exception as e:
print(e)
return {"statusCode": 500, "body": json.dumps({"message": "unexpected error"})}
return deco
@handle_exceptions
def my_expected_error_request(event, context):
raise BadRequest("This function raises a BadRequest with a 400 status code that should be sent to the user. The end user can read this text.")
@handle_exceptions
def my_unexpected_error_request(event, context):
raise Exception("Uh oh. I didn't expect this. A 500 error with an obfuscated message is raised. The end user cannot read this text.")
This pattern makes it very easy for an API to return the appropriate error message and status code. I have very rudimentary logging in this handle_exceptions
implementation but you can get really detailed messages with f.__name__
to know what Lambda function erred and the traceback module to understand the source of the exception. All this error management is completely hidden from the API user.