1

I am new to AWS API Gateway, I use it with AWS Lambda (Java).
Apparently the only way to change the response status code (to 4xx, 5xx, etc.) is to return a String that must match the Lambda error regex in the Integration Response section of the resource, or to throw an exception which also should contain a message matching the regex, is that right?

But then if I can only return a String, how do I provide more details for the response entity?
Here is what my RESTful APIs use to return along with an appropriate error status code:

{
    "code": "123",
    "message": "Invalid email address",
    "path": "/email"
}

The code property is useful for debugging or internationalization (i18n), the path is the field to correct.
Sometimes I wrap it in an "error" JSON object, sometimes I return a list of validation errors instead of only 1 error, etc. I customize it according to the project specs.

How can I produce such response entity with API Gateway?
Thanks.

EDIT: thanks to kennbrodhagen's advice, here is what I ended up doing:
1. I stringify my error object (using the Jackson ObjectMapper)
2. I throw a RuntimeException with the stringified error as the message
In the Integration Response:
3. I use some property of my error for the Lambda error regex
4. I mapped this template

{ "error": $input.path('$.errorMessage') }

Then the API response (if error occurs) becomes

{ "error": {"code": "123", ... }}

Very basic for now but better than just getting a String in the response.

Maxime Laval
  • 4,068
  • 8
  • 40
  • 60
  • Can you return HTTP status like 4XX or 5XX? I'm struggling with Java and status codes other than 200. If you have some time, please have a glance to my question here https://stackoverflow.com/questions/45200322/aws-gateway-always-returns-the-default-status-code. Thanks! – Perimosh Jul 20 '17 at 13:38

2 Answers2

2

Apparently the only way to change the response status code (to 4xx, 5xx, etc.) is to return a String that must match the Lambda error regex in the Integration Response section of the resource, or to throw an exception which also should contain a message matching the regex, is that right?

Correct.

In the target responses, you can configure mapping templates which can access any other part of the response from Lambda as well contextual variables, which are documented here: http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html

So you could set a mapping template on the 400 response under '/email'

{
    "code": "123",
    "message": "Invalid email address",
    "path": "$context.resourcePath"
}

Or something like that. That will be the only way to set up a custom response body for your Lambda error -> status code mappings.

jackko
  • 6,998
  • 26
  • 38
  • Hmm, my "path" property is not the resource path, it's the invalid field, it could be for instance "/password" if the password is invalid for a call to a resource "/register" for instance. Same as Spring MVC form validation if you are familiar with it, they use the word "path" to define what field in a form is incorrect. Also the same error status code may be linked to different "code" values in the JSON entity. So those properties have to be passed somehow from the Lambda handler to API Gateway... – Maxime Laval Feb 02 '16 at 02:58
2

I know you're working with Java but I'm more familiar with node so it was easier for me to prototype that way. Hopefully it will translate over without too much trouble.

Here is the prototype function I created in node. The goal is to return the err object as the JSON response body in the error. The status property will be used to map to the corresponding HTTP Status.

exports.handler = function(event, context) {
    var response = {
        status: 400,
        message: "Error details",
        path: "/x"
    }

    context.fail(JSON.stringify(response));

};

To use the status property value to map to an HTTP 400 and then return that entire structure as your JSON response do the following:

  1. In the Lambda when you pass your error to context.done or context.fail call JSON.stringify() on it to convert it to a JSON string. Since you're using Java I assume that you'll find a different way to transform your error into a JSON string equivalent.

  2. In the API Gateway Integration Response setup the mapping to use a regular expression that will match against the property that contains your status code in JSON format. In the example below I use .*"status":400.* to match against the status property. Note that I'm sending the code as a number so if you want to use a string you'll have to add quotes to the regex. Also note it's important to put the .* at the beginning and end of the regex so it will match on the substring.

  3. In the API Gateway Integration Response setup an application/json Content-Type and a mapping template that looks like this:

    $input.path('$.errorMessage')

enter image description here

Lambda takes what you pass into done/fail and calls toString() on it. It then assigns that string as the value of the errorMessage property in the JSON response it sends to API Gateway.

Next, API Gateway applies the regex to the value of the errorMessage property.

Finally, API Gateway executes your mapping template against the Lambda response string. The mapping template extracts the value from errorMessage and renders it as the response body.

To have the API Gateway return multiple different status results you'll need to setup a separate Integration Response mapping for each. You can copy/paste the same template into each.

kennbrodhagen
  • 4,258
  • 2
  • 26
  • 21