everythingpossible - Fotolia

Tip

Steps to generate an HTTP response header with AWS Lambda

Some AWS users have difficulty setting HTTP response headers based on Lambda functions. Chris Moyer explains how to accomplish that task, and how it can be helpful for developers.

Amazon API Gateway gives developers a simple way to access AWS Lambda functions through traditional REST requests. But questions remain about how to return responses from AWS Lambda functions to map values to HTTP headers. For example, a custom "Location" header, not just a reference in the body of the HTTP response, is required for AWS Lambda functions to return a redirect response.

The problem is that Amazon API Gateway is designed to allow for a great deal of response types and to be able to map an HTTP response header from existing AWS Lambda functions to return REST-style requests. This can make it complicated to use Amazon API Gateway exclusively for AWS Lambda functions, as the integration here is very abstracted, so developers have to jump through several hoops just to do something as simple as return a 302 redirect.

The most common mistake -- one I spent a few hours trying to figure out -- is not specifying the HTTP response header first in the "Method Response" section. You can't add a new header under just the "Integration Response" section; it must first be defined in "Method Response."

Add an alternate status code header

Here's a simple example of making a Lambda function return a 302 redirect. First, the developer needs to create a custom API using an Amazon API Gateway, a resource on that API and a method. For this, API, we'll use a GET method to simplify things.

Here's an example API configured to respond to "GET /stories":

Custom API configured to GET stories.
Establish a custom API to GET stories.

When we create the GET method, it gives an option of where that method should go. This first screen simply allows us to connect to a Lambda function:

Select an integration point for the method.
Select where the method will go.

The Lambda function -- or at least a stub of it -- needs to have been created before continuing. To keep things simple, create a Lambda function called "searchStories" using Node.js:

exports.handler = function handler(data, context){

  context.fail(‘http://google.com');

}

This simple function will toss a failure message that says "http://google.com", which we interpret as "redirect the user to http://google.com."

With that as the starting point, we can begin with our simple first-stage mapping. Enter "searchStories" in the Lambda function box on the GET /stories setup page and then save. A confirmation dialog will appear, allowing you to approve the permissions for Amazon API Gateway to access the Lambda function. After approval, the following screen appears:

A full view of the steps between the Lambda function and the end user.
Review and test the method and integration requests and responses.

Remember: This screen displays all the steps between the Lambda function and the end user. It also contains a small "Test" button, shown as a lightning bolt, which should be used several times to make sure everything is functioning properly once the Lambda function is set up.

On this screen, choose "Method Response" and then click on "Add Response."

The HTTP header status of the Method Response.
Select 'Add Response.'

On the next page, enter "302" -- or whatever status code is preferred -- and click the checkmark on the right. Pressing enter may mess up the console page, so if this happens refresh the browser to try again.

Enter the preferred status code and click the checkmark.
Click the checkmark on the right, do not press enter.

After that's done, click the small arrow next to the status response, and select "Add Header."

Add an HTTP response header.
Select 'Add Header' from the drop-down menu.

Just like with the HTTP status code, a small dialog box will appear to enter the location, then click the checkmark. Once that is set, click back to the GET method where the page will display several boxes of different parts of the Amazon API Gateway process. This time, choose "Integration Response."

Add an integration response.
After adding a Method Response, enter an Integration Response.

Under "Lambda Error Regex" enter http(s)?.//* and choose response status code 302.

Fill in Integration Response details.
Enter the Lambda Error Regex and Method Response Status.

Save and open the newly created 302 response status, and click to add a new header. Select the pencil icon beside the header "Location" and, under the mapping response, enter integration.response.body.errorMessage before clicking the circle check button:

Complete the header mapping.
Fill in the header mapping and click the checkmark button.

Choose "Save" and then go back to the main integration page and test the output. If everything works properly, it should look like this:

Test your output.
Confirm the test output matches expectations.

How to add a 200-level header

It can be less complicated to return a custom header for a normal 200-level HTTP response header. However, it is absolutely crucial to specify those available headers and map them directly in Amazon API Gateway. This makes it more difficult to add new headers but simpler than returning a custom status code.

Similar to the 302 status code, the developer must first specify the headers she wants to make available in the "Method Response" section, but this time under the 200 status code response handler, which is the default:

View the response header for the 200-level status code.
Specify the header you want to make available under the 200 status code.

In this case, the mapping is set up to return a custom x-result-count header. I use an x-result-count header in applications to return a custom header. The custom header indicates how many results a given search may contain. When setting up nonstandard HTTP headers, it's important to start with an x- to indicate it as such. Once that is set up, go to the "Integration Response" section and map the x-result-count header. This time, however, under this default status code mapping.

Map the output from the Lambda function.
Map the x-result-count header under the default status code.

Use the format of integration.response.body.[JSON Body value] to return a mapping from the response. In this example, I used integration.response.body.size which expects the Lambda function to return something like:

context.succeed({

  size: NumberOfResults,

  ...

});

For 200 status codes, we use context.succeed and can return full JSON-compatible objects. This makes it simple to support multiple response headers as well as additional body content to be returned to the user.

HTTP response header limitations

One major limitation is that you can't return JSON output from an error response or use anything other than an error response to modify the status code output. That means only the response in the header can be returned, not an error message as a header with an alternate status code, such as a 302 redirect.

On the first attempt, I tried to return something like "302: http://google.com." However, it returned a broken location redirect header because I couldn't find a way to only get the URL out of the error message in the mapping template. As a result, I modified my mapping to check for a URL as the response of the failure message. To return relative URLs, modify the mapping to check for other cases, or just change the default error to a 302 response.

Another big limitation is the requirement to predefine the headers that can be returned from the Lambda function. A developer cannot return something like this code:

headers: {

  Location: "http://google.com",

  RequestedBy: "user",

...

}

Instead, developers need to first define each of these headers in the Amazon API Gateway console. This makes for better documentation but slower development. Remember, if the response isn't coming out as expected, check the "Method Response" and then the "Integration Response" to see if the mapping in Amazon API Gateway is functioning properly. And use the test button after changing to the mappings.

Next Steps

Make SQS and Lambda play nice

Deploy microservices and Lambda in tandem

When to replace EC2 instances with Lambda functions

Dig Deeper on AWS cloud development