Intermediate
Serverless REST API
Build a complete CRUD API with API Gateway, Lambda, DynamoDB, and Cognito
Project Overview
Create a fully serverless backend API that handles CRUD operations with authentication. Perfect for mobile apps, SPAs, or any application needing a REST backend.
Prerequisites
- Basic understanding of REST API concepts
- Python or Node.js programming skills
- Completed Serverless Contact Form project
- Understanding of JSON and HTTP methods
Architecture
Client App
Web/Mobile
Cognito
Auth
API Gateway
REST API
Lambda
CRUD
DynamoDB
NoSQL
Fully serverless - pay only for what you use
Step-by-Step Instructions
1
Create DynamoDB Table
- Go to DynamoDB and create a new table
- Choose an appropriate partition key (e.g., "id" or "userId")
- Consider adding a sort key for queries
- Start with on-demand capacity mode
- Enable point-in-time recovery for production
2
Create Lambda Functions
- Create separate functions: createItem, getItem, updateItem, deleteItem, listItems
- Or create a single function that routes based on HTTP method
- Use the AWS SDK to interact with DynamoDB
- Return proper HTTP status codes and JSON responses
- Attach an IAM role with DynamoDB permissions
3
Set Up API Gateway
- Create a REST API in API Gateway
- Create resources (e.g., /items, /items/{id})
- Add methods: GET, POST, PUT, DELETE
- Enable Lambda proxy integration
- Configure request/response models (optional)
4
Configure Cognito Authentication
- Create a Cognito User Pool
- Configure sign-up and sign-in options
- Create an App Client
- Add a Cognito Authorizer in API Gateway
- Apply authorizer to protected endpoints
5
Deploy and Configure CORS
- Enable CORS on API Gateway resources
- Ensure Lambda returns CORS headers
- Deploy API to a stage (dev, prod)
- Note your API endpoint URL
6
Test with Postman or curl
- Test unauthenticated endpoints first
- Get a JWT token from Cognito
- Add Authorization header to requests
- Test all CRUD operations
- Verify data in DynamoDB console
Tips
- Use Lambda Layers for shared code - Share utilities and SDK clients across functions
- Enable DynamoDB Streams - Trigger additional processing when data changes
- Implement pagination - Use LastEvaluatedKey for listing endpoints
- Add request validation - Use API Gateway models to validate input
Code Examples
Lambda CRUD Handler (Python)
lambda_function.py
PYTHON
import json
import boto3
import uuid
from decimal import Decimal
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('Items')
def lambda_handler(event, context):
http_method = event['httpMethod']
try:
if http_method == 'GET':
if event.get('pathParameters'):
# Get single item
item_id = event['pathParameters']['id']
response = table.get_item(Key={'id': item_id})
return build_response(200, response.get('Item'))
else:
# List all items
response = table.scan()
return build_response(200, response.get('Items', []))
elif http_method == 'POST':
body = json.loads(event['body'])
body['id'] = str(uuid.uuid4())
table.put_item(Item=body)
return build_response(201, body)
elif http_method == 'PUT':
item_id = event['pathParameters']['id']
body = json.loads(event['body'])
body['id'] = item_id
table.put_item(Item=body)
return build_response(200, body)
elif http_method == 'DELETE':
item_id = event['pathParameters']['id']
table.delete_item(Key={'id': item_id})
return build_response(200, {'message': 'Deleted'})
except Exception as e:
return build_response(500, {'error': str(e)})
def build_response(status_code, body):
return {
'statusCode': status_code,
'headers': {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
'body': json.dumps(body, default=str)
}
DynamoDB Table (AWS CLI)
Terminal Commands
BASH
# Create DynamoDB table
aws dynamodb create-table \
--table-name Items \
--attribute-definitions AttributeName=id,AttributeType=S \
--key-schema AttributeName=id,KeyType=HASH \
--billing-mode PAY_PER_REQUEST
# Enable point-in-time recovery
aws dynamodb update-continuous-backups \
--table-name Items \
--point-in-time-recovery-specification PointInTimeRecoveryEnabled=true
IAM Policy for Lambda
lambda-dynamodb-policy.json
JSON
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:UpdateItem",
"dynamodb:DeleteItem",
"dynamodb:Scan",
"dynamodb:Query"
],
"Resource": "arn:aws:dynamodb:*:*:table/Items"
}
]
}
API Testing with curl
API Tests
BASH
API_URL="https://abc123.execute-api.us-east-1.amazonaws.com/prod"
# Create item
curl -X POST "$API_URL/items" \
-H "Content-Type: application/json" \
-d '{"name": "Test Item", "price": 29.99}'
# Get all items
curl "$API_URL/items"
# Get single item
curl "$API_URL/items/123-456-789"
# Update item
curl -X PUT "$API_URL/items/123-456-789" \
-H "Content-Type: application/json" \
-d '{"name": "Updated Item", "price": 39.99}'
# Delete item
curl -X DELETE "$API_URL/items/123-456-789"
What You'll Learn
- RESTful API design with API Gateway
- Lambda function patterns for CRUD operations
- DynamoDB data modeling and queries
- User authentication with Cognito
- API authorization strategies and JWT tokens