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
| Setting | Value |
|---|---|
| Integration type | AWS Service |
| AWS service | DynamoDB |
| Actions | PutItem, GetItem, Scan |
| Execution role | API 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:
Scanreads the entire table. For production APIs, preferQuerywith 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
| Route | DynamoDB action | Path param | Key mapping |
|---|---|---|---|
PUT /users | PutItem | — | Body → Item attributes |
GET /users/{id} | GetItem | id | $input.params('id') |
GET /users | Scan | — | Table 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.