AWS: Serverless REST API (API Gateway + Lambda + DynamoDb + WAF)
This project demonstrates a secure, scalable, and cost-efficient serverless REST API built using API Gateway, Lambda, DynamoDB, and protected by WAF. The architecture ensures high availability, automatic scaling, and IP-based access control for write operations, making it production-ready
Architecture:
Client → API Gateway → Lambda → DynamoDB
🟢 STEP 1: Create DynamoDB Table:
> Go to AWS Console
> Search → DynamoDB
> Click Create table
Fill:
Table name: users
Partition key:
Name: id
Type: String
Keep On-demand capacity
> Click Create table
✅ Done.
🟢 STEP 2: Create IAM Role for Lambda:
> Go to IAM
> Click Roles
> Click Create role
> Select:
Trusted entity: AWS Service
Use case: Lambda
> Click Next
> Attach policies:
AWSLambdaBasicExecutionRole
AmazonDynamoDBFullAccess
(For learning project. In real production, use limited custom policy.)
> Name role:
lambda-dynamodb-role
> Click Create role
✅ Done.
🟢 STEP 3: Create Lambda Function
> Go to Lambda
> Click Create function
> Choose:
Author from scratch
> Fill:
Function name: users-api-function
Runtime: Python 3.10 or 3.11
> Execution role:
→ Use existing role
→ Select lambda-dynamodb-role
> Click Create function
🟢 STEP 4: Add Lambda Code :
Replace existing code with this:
==
import json
import boto3
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('users')
def lambda_handler(event, context):
method = event['httpMethod']
if method == 'POST':
body = json.loads(event['body'])
table.put_item(Item=body)
return {
'statusCode': 200,
'body': json.dumps('User created')
}
elif method == 'GET':
user_id = event['queryStringParameters']['id']
response = table.get_item(Key={'id': user_id})
item = response.get('Item', {})
return {
'statusCode': 200,
'body': json.dumps(item)
}
elif method == 'PUT':
body = json.loads(event['body'])
user_id = body['id']
table.update_item(
Key={'id': user_id},
UpdateExpression="set info=:i",
ExpressionAttributeValues={
':i': body['info']
}
)
return {
'statusCode': 200,
'body': json.dumps('User updated')
}
elif method == 'DELETE':
user_id = event['queryStringParameters']['id']
table.delete_item(Key={'id': user_id})
return {
'statusCode': 200,
'body': json.dumps('User deleted')
}
else:
return {
'statusCode': 400,
'body': json.dumps('Unsupported method')
}
==
> Click Deploy
✅ Lambda ready.
🟢 STEP 5: Create API Gateway:
> Go to API Gateway
> Click Create API
> Choose REST API
> Click Build
> Fill:
API Name: users-api
Endpoint Type: Regional
> Click Create API
🟢 STEP 6: Create Resource:
> Click Actions → Create Resource
Resource name: users
Resource path: /users
> Click Create Resource
🟢 STEP 7: Add Methods:
Inside /users resource:
Add these methods:
GET
POST
PUT
DELETE
For each method:
> Click Create Method
> Select method (e.g., GET)
Integration type: Lambda Function
Check: Use Lambda Proxy Integration
Choose region
Select: users-api-function
Save
> Click OK to allow permissions
==> Repeat for all 4 methods.
🟢 STEP 8: Enable CORS:
> Select /users
> Click Actions → Enable CORS
> Confirm
🟢 STEP 9: Deploy API:
> Click Actions → Deploy API
> Deployment stage:
New Stage
Stage name: dev
> Click Deploy
You will get:
Invoke URL like:
https://abcd1234.execute-api.us-east-1.amazonaws.com/dev
Your endpoint becomes:
https://abcd1234.execute-api.us-east-1.amazonaws.com/dev/users
🟢 STEP 10: Test Using curl :
🔹 1️⃣ Create User (POST)
# curl -X POST https://YOUR_URL/dev/users -H "Content-Type: application/json" -d '{"id":"123","name":"Alice","info":"Tester"}'
Ex:
# shubham@7cf34de6dc20 ~ % curl -X POST https://duq42x1irl.execute-api.us-east-1.amazonaws.com/dev/users -H "Content-Type:\ application/json" -d '{"id":"123","name":"Alice","info":"Tester"}'
Expected Output:
"User created"%
🔹 2️⃣ Get User (GET)
# curl -X GET "https://YOUR_URL/dev/users?id=123"
Ex:
# curl -X GET "https://duq42x1irl.execute-api.us-east-1.amazonaws.com/dev/users?id=123"
Expected Output:
{"id": "123", "info": "Tester", "name": "Alice"}%
AWS console (Dynamodb Table explore) :
Browser:
🔹 3️⃣ Update User (PUT)
# curl -X PUT https://YOUR_URL/dev/users -H "Content-Type: application/json" -d '{"id":"123","info":"Updated Info"}'
Ex:
# curl -X PUT https://duq42x1irl.execute-api.us-east-1.amazonaws.com/dev/users -H "Content-Type:\ application/json" -d '{"id":"123","info":"Updated info"}'
Expected Output:
"User updated"%
AWS console (Dynamodb Table explore) :
🔹 4️⃣ Delete User (DELETE)
# curl -X DELETE "https://YOUR_URL/dev/users?id=123"
Ex:
# curl -X DELETE "https://duq42x1irl.execute-api.us-east-1.amazonaws.com/dev/users?id=123"
Expected Output:
"User deleted"%
🔹 Verify deleted entry:
# curl -X GET "https://YOUR_URL/dev/users?id=123"
Ex:
curl -X GET "https://duq42x1irl.execute-api.us-east-1.amazonaws.com/dev/users?id=123"
Expected Output:
{}%
AWS console (Dynamodb Table explore) :
Browser:
🟢 Final Testing Flow
POST → GET → PUT → GET → DELETE → GET
🎯 What You Just Built
> Fully serverless API
> No EC2
> Auto scaling
> Pay per request
> Production-grade architecture
> Cost effective and no infra to manage
======================================================
Now, lets make this more secure by WAF.
🏗 Architecture After WAF
Client → WAF → API Gateway → Lambda → DynamoDB
A Web Application Firewall (WAF) is a security solution that filters, monitors, and blocks HTTP/HTTPS traffic to and from web applications, operating at Layer 7 of the OSI model. It acts as a reverse proxy, shielding applications from attacks like SQL injection, cross-site scripting (XSS), and file inclusion.
🟢 STEP 1: Create IP Set:
> Go to AWS Console
Search → WAF & Shield
> Click IP sets
> Click Create IP set
> Fill:
Name: allowed-post-put-ips
Region: Same as API (important)
IP version: IPv4
Add addresses:
Example:
1.2.3.4/32
5.6.7.8/32
> Click Create IP set
✅ Done.
🟢 STEP 2: Create Web ACL :
> Go to Web ACLs
> Click Create web ACL
> Fill:
Name: users-api-waf
Region: Same region as API
Resource type: Regional resources
Select resource:
> Choose your API Gateway stage (example: users-api / dev)
> Click Next
🟢 STEP 3: Add Rules:
> Click Add rules
> Choose:
➜ Add my own rules
→ Rule builder
🔹 Create Rule: Allow Specific IPs
Rule name: allow-approved-ips
Rule type: IP set
Select IP set: allowed-post-put-ips
Action: Allow
Save rule.
🔹 Add Block Rule (Default)
After adding allow rule:
Set Default action = Block
This means:
Only listed IPs allowed
All others blocked
🟢 STEP 4: Review & Create:
> Click:
Next → Next → Create Web ACL
🎯 Now What Happens?
If request comes from:
IP : 1.2.3.4
Result : Allowed
IP : 5.6.7.8
Result : Allowed
IP: Any other
Result: 403 Forbidden
For ex:
eshubhm@7cf34de6dc20 ~ % curl -X POST https://duq42x1irl.execute-api.us-east-1.amazonaws.com/dev/users -H "Content-Type:\ application/json" -d '{"id":"123","name":"Alice","info":"Tester"}'
Expected output from non-allowed IPs:
{"message":"Forbidden"}%
⚠ Important
> WAF works at full API level.
> If you want:
Only restrict POST and PUT
Allow GET for everyone
We need advanced rule using HTTP method condition
> WAF is NOT free.
Approx:
~$5 per Web ACL
~$1 per rule
> request charges
For learning → OK
For production → Worth it
=======================================================================
Thank you, check out more blogs for such information..!
For any queries,feel free to reach out me on shubhammore07007@gmail.com
Comments
Post a Comment