API Gateway Mapping Templates for DynamoDB

Overview

Use API Gateway as a proxy to DynamoDB with Velocity Template Language (VTL) mapping templates. API Gateway transforms HTTP requests into DynamoDB API calls and maps responses back to JSON for clients.

Reference: Using Amazon API Gateway as a proxy for DynamoDB

Prerequisites

SettingValue
Integration typeAWS Service
AWS serviceDynamoDB
ActionsPutItem, GetItem, Scan
Execution roleAPI Gateway IAM role with DynamoDB permissions

Table: Users

Partition key: userId (String)

Create user — PUT /users

Request

PUT /users
Content-Type: application/json
{
  "userId": "u123",
  "name": "Mausam",
  "age": 24
}

Integration request mapping template

Set Content-Type to application/json, then use this VTL for DynamoDB PutItem:

{
  "TableName": "Users",
  "Item": {
    "userId": { "S": "$input.path('$.userId')" },
    "name": { "S": "$input.path('$.name')" },
    "age": { "N": "$input.path('$.age')" }
  }
}

Integration response mapping template

DynamoDB PutItem returns {}. Return a friendly message to the client:

{
  "message": "User created successfully"
}

Get user — GET /users/{id}

Request

GET /users/u123

Path parameter: id = u123

Integration request mapping template

{
  "TableName": "Users",
  "Key": {
    "userId": { "S": "$input.params('id')" }
  }
}

DynamoDB response (raw)

{
  "Item": {
    "userId": { "S": "u123" },
    "name": { "S": "Mausam" },
    "age": { "N": "24" }
  }
}

Integration response mapping template

Flatten DynamoDB attribute types into plain JSON:

{
  "userId": "$input.path('$.Item.userId.S')",
  "name": "$input.path('$.Item.name.S')",
  "age": $input.path('$.Item.age.N')
}

Client response

{
  "userId": "u123",
  "name": "Mausam",
  "age": 24
}

List users — GET /users

Request

GET /users

Integration request mapping template

{
  "TableName": "Users"
}

Use DynamoDB action Scan for this route.

Integration response mapping template

#set($items = [])

#foreach($item in $input.path('$.Items'))
  #set($dummy = $items.add({
    "userId": $item.userId.S,
    "name": $item.name.S,
    "age": $item.age.N
  }))
#end

$util.toJson($items)

Client response

[
  {
    "userId": "u123",
    "name": "Mausam",
    "age": 24
  }
]

Note: Scan reads the entire table. For production APIs, prefer Query with a key condition or use a GSI.

IAM policy for API Gateway

Grant the API Gateway execution role access to the Users table:

{
  "Effect": "Allow",
  "Action": [
    "dynamodb:PutItem",
    "dynamodb:GetItem",
    "dynamodb:Scan"
  ],
  "Resource": "arn:aws:dynamodb:us-east-1:ACCOUNT_ID:table/Users"
}

Step Functions integration

Integration request mapping template

Start a state machine execution from API Gateway:

  • #1
{
  "input": "$util.escapeJavaScript($input.body)"
  "stateMachineArn": "arn:aws:states:REGION:ACCOUNT_ID:stateMachine:STATE_MACHINE_NAME",
}
  • #2
#set($rawRequest = $input.json('$'))
{
  "input": "$util.escapeJavaScript($rawRequest)",
  "stateMachineArn": "arn:aws:states:us-east-1:271995869266:stateMachine:express-stat-flow"
}

Integration response mapping template

Parse and return the Step Functions output:

To get the specific property from the response. eg output try:

$input.path('$.output')

this converts the response into JAVA Map Object (avoid)

#set($output = $util.parseJson($input.path('$.output')))
$output

Quick reference

RouteDynamoDB actionPath paramKey mapping
PUT /usersPutItemBody → Item attributes
GET /users/{id}GetItemid$input.params('id')
GET /usersScanTable name only

DynamoDB type suffixes: S = String, N = Number, B = Binary. VTL maps client JSON into these typed attributes on the way in, and flattens them on the way out.

Ref: https://medium.com/@kruchkov.alexandr/a-deep-dive-into-aws-api-gateway-velocity-mapping-templates-9a6c9f4ed742