Skip to main content
AWS API Gateway and SNS

AWS API Gateway - Request Response Mapping

AWS API Gateway is a very useful service to put a proxy in front of your backend API, or even have a Lambda function without any backend endpoints. It does support a lot of AWS services. In this blog I am going to talk about how you can do body mapping for the input request of the response coming back from the backend endpoint. It uses VTL language.

If you wonder how this will be useful, it could cut down the unnecessary endpoint for doing that job. Let’s say you have a search autocomplete feature where you need to receive GET requests and then form that input into a complex input for our search engine (ElasticSearch or SOLR or CloudSearch) like removing certain words, build a custom query with additional conditions. You will need a intermediate processing that does it for you say Lambda function. Instead of that, you can use the Mapping feature of API Gateway to do the conversion. Let's see how to do that.

Step 1: Build a new endpoint with GET method in API Gateway.

Step 2: Map the input request parameter, eg below

Request param

Step 3: Go to Integrations request->Body Mapping Templates

 

Mapping

I choose “Never” to indicate that the request should always be transformed, else do not pass it. Now click the “application/json” or create new Content Type mapping, now comes the important part, where you need to write VTL code do the processing.

Request Mapping Code

## Set the variable inputRoot to the root JSON document

{

#set ($aptparams = ['APT','LOT'])

#set ($address = $input.params("address").toUpperCase())

#set ($address = $address.replaceAll(" +", " "))

#set ($address = $address.replace(".", ""))

#set ($address = $address.replace(",", " "))

#set ($replace = 0)

#set ($myStopWords = {

"LN" : "LANE",

'ROAD':'RD',

'STREET':'ST',

'DRIVE ':'DR',

})



##$address.replaceAll("LN\s*$", " ")

#foreach($stopKey in $myStopWords.keySet())

   #set($replacement = $myStopWords.get($stopKey))

   #set($address = $address.replace(" $stopKey ", " "))

   #set($address = $address.replaceAll("$stopKey\s*$", ""))

   #set($address = $address.replace($replacement, " "))

   #set($address = $address.replaceAll("$replacement\s*$", ""))

#end

#foreach($apt in $aptparams)

   #set($address = $address.replace(" $apt ", " "))

   #set($address = $address.replaceAll("$apt\s*$", ""))

#end

#set ($address = $address.toUpperCase())



"query": {

 "bool": {

   "must": {

     "match_phrase": {

       "address": {"query":"$address","slop":6}

     }

   },

    "must_not": {

     "match": {

       "status": "Inactive"

     }

   },

   "filter": {

     "term": {

       "zip": "$input.params('zip')"

     }

   }

  }

}

}



$input.params("address") gets the GET param from the original request. VTL supports various string functions like,

  • toUpperCase
  • toLowerCase
  • contains
  • equals

From the code I have posted above you could see how to use for loop, setting variables and comment.

Some tips,

  1. ## means comment

  2. # is a command

  3. Foreach or if should end with #end

Remember not all the VTL constructs are supported in AWS API Gateway, for eg see the response I received the AWS Support team for using .isNumber(),

API Gateway doesn't support all the constructs of VTL. It only supports a very specific set of constructs which are provided in VTL. So methods like "$String.isNumber()", "$Integer.parseInt()" etc., are not supported by API Gateway.

VTL Guide: http://velocity.apache.org/engine/1.7/user-guide.html

Step 4: You could also map the response coming back from the backend API.

Response mapping

Now click the Content Type required to write your VTL code the processing. See the sample I have written,

#set($hits=$input.json("$.hits.hits"))

[

#foreach ($hit in $util.parseJson($hits))

{

  #set($hitData=$hit.get('_source'))

  #foreach($paramName in $hitData.keySet())

     "$paramName" : "$util.escapeJavaScript($hitData.get($paramName))"

     #if($foreach.hasNext),#end

   #end

  

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

#end

]

I receive the “hits” inside the “hits” object of the original response we receive as an input. Then loop over the records, get the _source object inside each record, and build a new JSON output.

This is needed to format the ElasticSearch search response to be easily accessible by the client libraries. That's all, your input and output are preprocessed now. You can use "Test" feature of API Gateway to debug how the mapping works. Let me know if you run into any trouble.

 

Add new comment

The content of this field is kept private and will not be shown publicly.

Plain text

  • No HTML tags allowed.
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.