Tips and Hacks for working with AWS Lambda and API Gateway

So if you’ve seen my articles before chances are you know I started a tutorial on setting up a micro-services application using both Amazon’s Lambda and API Gateway services and well I got a little stuck. I ended up worrying that the final part would not be all that great and that API Gateway itself was a bit of a flawed to use for production applications. I’m still not quite sure on the last part but over time I have found some work arounds and fixes for those issues that might block you from getting your API the way you want it.

Request Body can be other than JSON

One of the little things I wanted to try was what if I could accept a form request e.g. the classic application/x-www-form-urlencoded body content type. The short answer is yes, you can do so, you could even use application/xml or any other format from your request but it comes at a price.

API Gateway is built for JSON APIs in a fairly half hearted I’m-not-sure-why-system. So you can create a mapping template using this doc as a reference. So you might create a template like they give as an example:

{
"body" : $input.json('$')
}

Now, you send a typical application/JSON body…

{
"foo": "bar"
}

What you’ll get is a JSON object under the body key:

{
"body" : {
"foo": "bar"
}
}

But if you send a different format, say some XML you’ll receive the body key as a string:

{
"body" : "<test>my request body</test>"
}

In doing so you can use Lambda to parse the body string for you and that’s awesome because you’re open to using any content type request bodies you like.

One major pitfall (Well it can’t all be that simple right…?). What API Gateway will mess up is if your request body is remotely thought to be in JSON it will process it as such and return an error you can’t even hide or change the format of.

For example, you send the following (non sensical format I know, it’s an example) as your request body:

{<test>my request body</test>}

And then the instance response to this is:

{
"message": "Could not process payloadnet.minidev.json.parser.ParseException: Unexpected End Of File position 30: null"
}

It seems like a stupid thing to be annoyed about as it’s won’t effect you too much but it is a pain non the less as the whole point is you want to make your API and right now you have to be careful when it comes to your request body. Even if you want to use JSON and the request body isn’t formatted correctly this will happen as well wether you choose to use the $input.json(x) provider in your request template or not.

Custom Headers in Responses

So this one is less of a tip and more so an undocumented feature, as of January 2016 anyways. Being able to create custom headers is incredibly useful, for starters it allows use to create dynamic Access-Control-Allow-Origin or even Location headers to do redirects. What we need to do is simply add a reference to our response mapping to tell API Gateway which key from the Lambda response object to use.

integration.response.body.field

All that needs to be done is to put a response header under the status code you want using the Method Response page for the method. Then under the Integration Response page you can add a header mapping which will take the Lambda success object’s key to populate the header value. So if the key in our success JSON is called foo then we need to use integration.response.body.field as our non-static value.

Error Mapping from Lambda to Status Codes

Please bare in mind this is a hack and may not be production worthy.

I’ll keep this one as short as possible. I experienced some issues here and sadly didn’t know about this article when I was fighting with API Gateway to make detailed error responses to work. The article is pretty good at explaining the ins and outs of why we have to use such a hack so if you want the full details please read it, otherwise here’s the best way to hack a fix around it.

To start you can’t give detailed error messages in API Gateway by which I mean you’re stuck to using a single string which also has to have a reliable pattern to it otherwise it’ll be picked up as a successful response e.g. your HTTP 200.

If you did read the article I linked at the start of this then you’ll know that there’s no JSON parse method in the template engine used by API Gateway, but in fact you can still make it work namely with the following template:

## Parse JSON String to return a JSON body
#set($lastChar = $input.json(‘$.errorMessage’).length() — 2)
#set($body = $input.json(‘$.errorMessage’).substring(1, $lastChar))
$body.replace(‘\”’,’”’)

So now if we have a function in Lambda calling context.fail with a JSON object converted to a string as a parameter.

exports.handler = function(event, context) {
var error = {
code: “NotFound”,
message: “The requested resource was not found”
};
context.done(JSON.stringify(error));
};

Then we can use a regex like .*\”code\”: \”NotFound\”.* to give us the right status code in this case a 404. And now when we test our application we’ll actually get a full JSON response with no parsing the string on the client side as had been suggested in the past.

How this template works is it’s using the Java string methods available through the Velocity template system. We know an error from Lambda will give us a JSON object with the key errorMessage and in turn we know that it’s been made JSON safe coming from Lambda so we remove the start and end quotes with the substring and length function and then simply do a replace to turn our escaped \” back into .

One Downside to this is you can’t make use of the error message to easily map a header value still. The header mapping is done separate to the template so you won’t suddenly be able to use the result of response body to make a header template. Though I maybe wrong, you can always keep trying and if you find any interesting work arounds please let me know!