You are viewing a free preview of this lesson.
Subscribe to unlock all 10 lessons in this course and every other course on LearningBro.
Connecting API Gateway to Lambda is the most common serverless pattern on AWS. This lesson covers integration types, request/response mapping, proxy vs custom integrations, error handling, and building a complete REST API backed by Lambda functions.
API Gateway supports several integration types that determine how requests flow to your backend:
| Integration Type | Description | When to Use |
|---|---|---|
Lambda Proxy (AWS_PROXY) | Passes the entire request to Lambda as-is | Default choice — simplest approach |
Lambda Custom (AWS) | Uses mapping templates to transform request/response | When you need to reshape data between API GW and Lambda |
HTTP Proxy (HTTP_PROXY) | Forwards request to an HTTP endpoint | Proxying to existing HTTP services |
HTTP Custom (HTTP) | Like HTTP proxy but with mapping templates | Transforming requests to legacy APIs |
Mock (MOCK) | Returns a response without calling any backend | Health checks, CORS preflight, stubs |
Lambda proxy integration is the simplest and most common pattern. API Gateway forwards the entire HTTP request to Lambda in a standardised format.
{
"resource": "/users/{userId}",
"path": "/users/123",
"httpMethod": "GET",
"headers": {
"Accept": "application/json",
"Authorization": "Bearer eyJ..."
},
"queryStringParameters": { "include": "orders" },
"pathParameters": { "userId": "123" },
"body": null,
"isBase64Encoded": false,
"requestContext": {
"accountId": "123456789012",
"apiId": "abc123",
"stage": "prod",
"requestId": "req-abc-123",
"identity": {
"sourceIp": "203.0.113.42",
"userAgent": "Mozilla/5.0..."
}
}
}
export const handler = async (event) => {
const userId = event.pathParameters.userId;
const user = await getUserById(userId);
if (!user) {
return {
statusCode: 404,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({ error: 'User not found' }),
};
}
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify(user),
};
};
Important: The
bodymust always be a string — useJSON.stringify()for JSON responses. Returning an object directly will cause a 502 error.
| Field | Required | Description |
|---|---|---|
statusCode | Yes | HTTP status code (200, 404, 500, etc.) |
headers | No | Response headers (including CORS) |
multiValueHeaders | No | Headers with multiple values (e.g., Set-Cookie) |
body | No | Response body (must be a string) |
isBase64Encoded | No | Set to true for binary responses |
Custom integrations use Velocity Template Language (VTL) mapping templates to transform requests before they reach Lambda and responses before they return to the client.
## Transform API Gateway request into a simpler Lambda input
{
"userId": "$input.params('userId')",
"action": "$context.httpMethod",
"queryParams": {
#foreach($param in $input.params().querystring.keySet())
"$param": "$input.params().querystring.get($param)"
#if($foreach.hasNext),#end
#end
},
"requestId": "$context.requestId"
}
## Transform Lambda response for the API client
#set($response = $input.path('$'))
{
"data": $response.body,
"metadata": {
"requestId": "$context.requestId",
"timestamp": "$context.requestTime"
}
}
| Scenario | Recommendation |
|---|---|
| Simple API with Lambda | Proxy integration |
| Need to reshape request/response | Custom integration |
| Legacy Lambda expecting specific format | Custom integration |
| Decoupling API contract from Lambda logic | Custom integration |
| Rapid prototyping | Proxy integration |
Let us build a CRUD API for managing products.
/products
GET -> listProducts -> Scan DynamoDB table
POST -> createProduct -> PutItem to DynamoDB
/products/{productId}
GET -> getProduct -> GetItem from DynamoDB
PUT -> updateProduct -> UpdateItem in DynamoDB
DELETE -> deleteProduct -> DeleteItem from DynamoDB
Instead of one Lambda per endpoint, use a single function that routes internally:
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import {
DynamoDBDocumentClient, GetCommand, PutCommand,
ScanCommand, UpdateCommand, DeleteCommand
} from '@aws-sdk/lib-dynamodb';
Subscribe to continue reading
Get full access to this lesson and all 10 lessons in this course.