AWS Builder Center
Building an Event-Driven REST API with AWS Lambda, API Gateway, and DynamoDB

Building an Event-Driven REST API with AWS Lambda, API Gateway, and DynamoDB

A step-by-step practical guide to deploying a production-ready serverless REST API using Amazon API Gateway HTTP APIs, AWS Lambda, and Amazon DynamoDB, including error handling and IAM security best practices.

Building a Resilient Serverless REST API with AWS Lambda, API Gateway, and DynamoDB

Overview

Modern cloud architectures prioritize decoupling, auto-scaling, and pay-per-use economics. In this walkthrough, we build a resilient CRUD endpoint using Amazon API Gateway (HTTP API), AWS Lambda, and Amazon DynamoDB for single-digit millisecond data persistence.

Architectural Breakdown

  • API Gateway (HTTP API): Low-latency entry point handling routing, throttling, and CORS.
  • AWS Lambda: Ephemeral compute running business logic on-demand.
  • Amazon DynamoDB: Fully managed NoSQL key-value store configured with on-demand capacity.

Prerequisites

  • An active AWS Account with permissions for Lambda, API Gateway, and DynamoDB.
  • AWS CLI installed and configured locally (aws configure).
  • Node.js (v18+) or Python installed.

Step 1: Create the DynamoDB Table

Set up an on-demand table to eliminate manual capacity planning:
1
2
3
4
5
aws dynamodb create-table \
--table-name ItemsTable \
--attribute-definitions AttributeName=itemId,AttributeType=S \
--key-schema AttributeName=itemId,KeyType=HASH \
--billing-mode PAY_PER_REQUEST

Step 2: Implement the Lambda Handler

Here is a clean Node.js handler using the modular AWS SDK v3:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import {
DynamoDBDocumentClient,
PutCommand,
GetCommand
} from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({});
const docClient = DynamoDBDocumentClient.from(client);

const TABLE_NAME = process.env.TABLE_NAME || "ItemsTable";

export const handler = async (event) => {
const method = event.requestContext?.http?.method;
const path = event.rawPath;

try {
if (method === "POST" && path === "/items") {
const body = JSON.parse(event.body || "{}");

await docClient.send(
new PutCommand({
TableName: TABLE_NAME,
Item: body,
})
);

return {
statusCode: 201,
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
message: "Item created successfully"
}),
};
}

if (method === "GET" && path.startsWith("/items/")) {
const itemId = path.split("/")[2];

const result = await docClient.send(
new GetCommand({
TableName: TABLE_NAME,
Key: { itemId },
})
);

if (!result.Item) {
return {
statusCode: 404,
body: JSON.stringify({
message: "Item not found"
}),
};
}

return {
statusCode: 200,
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(result.Item),
};
}

return {
statusCode: 400,
body: JSON.stringify({
message: "Unsupported route"
}),
};

} catch (err) {
console.error("Execution error:", err);

return {
statusCode: 500,
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
error: err.message
}),
};
}
};

Step 3: Best Practices & Security

1. Least-Privilege IAM Roles

Scope down the Lambda execution role.
Do not use managed administrator policies. Grant only the permissions required by the Lambda function, such as:
  • dynamodb:PutItem
  • dynamodb:GetItem
Restrict these permissions to the specific DynamoDB table ARN.

2. Cold Start Optimization

  • Keep dependencies minimal.
  • Use modern Lambda runtimes.
  • Avoid unnecessarily large external libraries.
  • Reuse SDK clients outside the Lambda handler so they can be reused across invocations.

3. Structured Observability

Log events as structured JSON objects to enable targeted queries in CloudWatch Logs Insights.
Example:
1
2
3
4
5
console.log(JSON.stringify({
level: "INFO",
action: "CREATE_ITEM",
timestamp: new Date().toISOString()
}));

Summary

This serverless architecture provides a highly scalable, low-maintenance API backend with minimal operational overhead.
The architecture can be extended by adding:
  • Amazon EventBridge for asynchronous event publishing.
  • AWS WAF to protect the public API.
  • Amazon CloudWatch for monitoring and observability.
  • Amazon Cognito for authentication and authorization.
  • Additional DynamoDB operations for a complete CRUD implementation.

Architecture Flow

1
2
3
4
5
6
7
8
9
10
11
12
13
Client
|
v
API Gateway (HTTP API)
|
v
AWS Lambda
|
v
Amazon DynamoDB
|
v
CloudWatch Logs
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article