Share

How to create a Request object for your Lambda event from API Gateway


 

Are you using API Gateway with Lambda? Do you wish you had a Request object with properties for headers, params, and query like Express does? This article is for you.

Mapping Template

To provide information from the API Gateway's HTTP request to your Lambda function you will use what is called a Mapping Template. This is a JSON-like document that will be rendered to produce the contents of the event object. The template is made using the Velocity Template Language created by Apache. I'll explain a bit about the template language later, first let's just use it to get your header information into your Lambda.

Edit the Mapping Template in your Integration Request

In your AWS Console open up your API Gateway and find the method you want to provide headers. Locate the Integration Request box and click on it to open up these settings.

Open Integration Request

Find the Mapping Templates area of the Integration request and open it up. Add a new mapping template for the application/json Content-Type. You'll need to change the type in the combo box from "Input passthrough" to "Mapping Template". You will add a mapping template that will provide the HTTP headers along with the original request body to your Lambda.

Create the application/json Mapping Template

Enter this into the text area for the Mapping Template:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"body" : $input.json('$'),
"headers": {
#foreach($header in $input.params().header.keySet())
"$header": "$util.escapeJavaScript($input.params().header.get($header))" #if($foreach.hasNext),#end

#end
},
"method": "$context.httpMethod",
"params": {
#foreach($param in $input.params().path.keySet())
"$param": "$util.escapeJavaScript($input.params().path.get($param))" #if($foreach.hasNext),#end

#end
},
"query": {
#foreach($queryParam in $input.params().querystring.keySet())
"$queryParam": "$util.escapeJavaScript($input.params().querystring.get($queryParam))" #if($foreach.hasNext),#end

#end
}
}

Save the mapping template. Now your Lambda function will recieve an event object with several new properties:

  • body contains the parsed JSON body of the request for PATCH/POST/PUT method requests.
  • headers contains all the HTTP headers that appeared in the API Gateway request.
  • method contains the HTTP method used to call the API. This property makes it easier to provide the same Lambda to several different API Gateway methods.
  • params contains the Request Path Parameters that you register with API Gateway by using curly-braces in the Method name.
  • query contains the URL Query String Parameters that you register with API Gateway in the Method Request portion of the API.

This interface isn't exactly the same as the Express Request object, but it captures the spirit and keeps the names consistent where it can. If you want more of the same properties you could add code to create them inside your Lambda by parsing the headers.

Updating your Lambda

Now that the API Gateway will pass these properties to your lambda, you can update it to reference them accordingly. The following simple Lambda handler shows how to log the values:

1
2
3
4
5
6
7
8
9
exports.handler = function(event, context) {
console.log('Body:', event.body);
console.log('Headers:', event.headers);
console.log('Method:', event.method);
console.log('Params:', event.params);
console.log('Query:', event.query);

context.succeed('Success');
};

Dissecting the Mapping Template

The mapping template has a lot going on inside it. I'll save the details of how it's generally used for another post, but I will explain what each line of the template does here.

1
"body" : $input.json('$'),

This line takes whatever was part of the original request body and maps it into the body property of the Lambda event object. This way if you're trying to use this in a POST or a PUT you will have access to the orginal request body in event.body. The function $input.json() is a function available in the AWS Velocity Template. The special '$' parameter tells it to pass the entire body instead of just a sub-path.

1
#foreach($header in $input.params().header.keySet())

This line iterates over each HTTP header in the request. It gets the headers from the $input.params() function and then provides the keySet() to the for loop to iterate over. These headers are assigned to the $header iterator variable. There is a similar version of this line for the Request Path and Query parameters.

1
"$header": "$util.escapeJavaScript($input.params().header.get($param))"

This line will be written once for each iteration of the foreach loop. It creates a JSON property for each $header, which is one of the HTTP header names. The value of the property is going to be the value of the HTTP header. You get this value using $input.params().header.get() and passing it in the key. The $util.escapeJavascript() function is used in case the header value contains a quote character that needs to be escaped. There is a similar version of this line for the Request Path and Query parameters.

1
#if($foreach.hasNext),#end

This #if statement checks to see if there will be another iteration of the for loop and prints a comma if there will be to keep the JSON valid. There is a similar version of this line for the Request Path and Query parameters.

1
#end

Finally, the foreach loop is ended with the #end statement. 3 versions of this line as well.

Resources

I used the following resources from AWS and Apache to come up with this mapping template. You may find them useful in customizing the template presented here:

Thanks for reading!

Were you able to use the information in this article? Please let me know! I welcome any & all feedback. You can reach me at kennbbrodhagen@gmail.com or on Twitter @kennbrodhagen.


 

Interested in more?

Sign up for my mailing list. You'll get each new article and other announcements delivered to your inbox. I promise not to spam you. Unsubscribe any time you like.