AWS Builder Center
Building a Serverless REST API with Amazon API Gateway, AWS Lambda, and DynamoDB

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

Learn how to build a fully serverless REST API using Amazon API Gateway, AWS Lambda, and Amazon DynamoDB. This article walks through the architecture, implementation, IAM permissions, and deployment considerations for a simple CRUD-style API without managing servers.

Introduction

Building and operating APIs traditionally requires provisioning servers, managing operating systems, configuring load balancers, and planning for scaling.
AWS serverless services allow us to focus on application logic instead of infrastructure management.
In this tutorial, we'll build a simple REST API using three AWS services:
  • Amazon API Gateway — exposes HTTP endpoints.
  • AWS Lambda — executes our application logic.
  • Amazon DynamoDB — stores application data.
The resulting architecture looks like this:
1
2
3
4
5
6
7
8
9
10
Client
|
v
API Gateway
|
v
AWS Lambda
|
v
DynamoDB

1. Create a DynamoDB Table

First, create a DynamoDB table to store our data.
For this example, we'll create a Users table with userId as the partition key.
Example item:
1
2
3
4
5
{
"userId": "123",
"name": "John Doe",
"email": "john@example.com"
}
Choosing a good partition key is important because DynamoDB uses it to distribute data across its underlying infrastructure.

2. Create the Lambda Function

Next, create an AWS Lambda function.
For example, using Python:
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
import json
import os
import boto3

dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table(os.environ["TABLE_NAME"])

def lambda_handler(event, context):
user_id = event["pathParameters"]["userId"]

response = table.get_item(
Key={"userId": user_id}
)

item = response.get("Item")

if not item:
return {
"statusCode": 404,
"body": json.dumps({
"message": "User not found"
})
}

return {
"statusCode": 200,
"body": json.dumps(item)
}
The function receives the userId from the API request, queries DynamoDB, and returns the result.

3. Configure IAM Permissions

Lambda needs permission to read from DynamoDB.
A minimal IAM policy could look like this:
1
2
3
4
5
6
7
8
9
10
11
12
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:GetItem"
],
"Resource": "arn:aws:dynamodb:REGION:ACCOUNT_ID:table/Users"
}
]
}
Following the principle of least privilege is important.
If the Lambda function only needs GetItem, there is no reason to grant it unrestricted DynamoDB permissions.
Avoid overly broad permissions such as:
1
2
3
4
5
{
"Effect": "Allow",
"Action": "dynamodb:*",
"Resource": "*"
}
unless there is a specific requirement for them.

4. Create the API Gateway Endpoint

Now we can expose the Lambda function through Amazon API Gateway.
For example:
1
GET /users/{userId}
A request could look like:
1
GET https://api.example.com/users/123
API Gateway receives the request and invokes the Lambda function.
The complete request flow becomes:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
HTTP Client
|
| GET /users/123
v
API Gateway
|
| Invoke Lambda
v
AWS Lambda
|
| GetItem
v
DynamoDB
|
| User record
v
AWS Lambda
|
| JSON response
v
API Gateway
|
v
HTTP Client

5. Add More API Operations

Once the basic endpoint works, we can extend the API.
MethodEndpointPurpose
GET/users/{userId}Retrieve a user
POST/usersCreate a user
PUT/users/{userId}Update a user
DELETE/users/{userId}Delete a user
You can implement these operations using a single Lambda function or separate functions, depending on the application's architecture and requirements.

6. Use Environment Variables

Instead of hardcoding the DynamoDB table name inside the application, use a Lambda environment variable:
1
TABLE_NAME=Users
Then retrieve it in Python:
1
2
table_name = os.environ["TABLE_NAME"]
table = dynamodb.Table(table_name)
This makes the same application code easier to deploy across development, staging, and production environments.
For sensitive values, consider using AWS Secrets Manager or AWS Systems Manager Parameter Store instead of storing secrets directly in environment variables.

7. Add Observability

A serverless application still needs monitoring.
AWS Lambda integrates with Amazon CloudWatch, allowing you to monitor:
  • Function logs
  • Invocation counts
  • Errors
  • Duration
  • Throttles
  • Metrics
For example, if your API starts returning HTTP 500 responses, CloudWatch logs can help identify whether the problem is in your Lambda code or its interaction with DynamoDB.
For distributed applications, AWS X-Ray can also help trace requests across supported AWS services.

8. Security Considerations

Before putting the API into production, consider authentication and authorization.
Depending on your application, you can integrate API Gateway with authentication services such as Amazon Cognito or implement another appropriate authorization mechanism.
You should also consider:
  • Least-privilege IAM policies
  • HTTPS
  • Input validation
  • API throttling
  • Authentication and authorization
  • DynamoDB access patterns
  • CloudWatch monitoring and alarms
  • Separate environments for production workloads

9. Use Infrastructure as Code

Manually creating resources through the AWS Management Console is useful for learning, but infrastructure becomes easier to reproduce when it is defined as code.
Common AWS options include:
  • AWS CloudFormation
  • AWS SAM
  • AWS CDK
For example, AWS SAM can define a Lambda function, API Gateway endpoint, and DynamoDB table in a template and make deployments repeatable.
Infrastructure as Code also makes infrastructure changes easier to review and integrate into CI/CD pipelines.

Conclusion

Amazon API Gateway, AWS Lambda, and DynamoDB provide a powerful foundation for building APIs without managing traditional servers.
The basic architecture is simple:
1
API Gateway → Lambda → DynamoDB
The real value comes from combining these services with good engineering practices such as:
  • Least-privilege IAM
  • Infrastructure as Code
  • Observability
  • Input validation
  • Authentication
  • Automated deployments
Once the basic API is working, you can extend the architecture with authentication, automated deployments, custom domains, monitoring, caching, and additional AWS services as your application grows.
This serverless approach can be a useful foundation for prototypes, event-driven applications, and APIs with variable traffic while reducing the amount of infrastructure that developers need to manage.
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