Serverless Event Processing
AWS architecture template · SAA-C03: WAF + API Gateway → Lambda → SQS → Lambda Worker → DynamoDB + SNS
Serverless Event Processing is a free AWS architecture template built with IAM, Lambda, API Gateway, DynamoDB, SNS, SQS and WAF. Open it in TheArchitect to edit the diagram, estimate monthly cost, and export the CloudFormation or Terraform that deploys it.
AWS services in this template
- IAM×2
- Lambda×2
- API Gateway
- DynamoDB
- SNS
- SQS
- WAF
Deployment notes
Deploy-ready
- Includes inline Lambda code, IAM execution roles, SQS queue, DynamoDB table, and SNS alert topic.
Generated CloudFormation — 13 resources
| Logical ID | Resource type |
|---|---|
| WAFwaf | AWS::WAFv2::WebACL |
| APIGatewayapigateway | AWS::ApiGateway::RestApi |
| ProcessorLambdalambda | AWS::Lambda::Function |
| ProcessorRoleiam | AWS::IAM::Role |
| EventQueuesqs | AWS::SQS::Queue |
| WorkerLambdalambda | AWS::Lambda::Function |
| WorkerRoleiam | AWS::IAM::Role |
| EventsTabledynamodb | AWS::DynamoDB::Table |
| AlertTopicsns | AWS::SNS::Topic |
| ProcessorLambdalambdaEventQueuesqsEventSourceMapping | AWS::Lambda::EventSourceMapping |
| WorkerLambdalambdaEventQueuesqsEventSourceMapping | AWS::Lambda::EventSourceMapping |
| WorkerLambdalambdaAlertTopicsnsPermission | AWS::Lambda::Permission |
| AlertTopicsnsWorkerLambdalambdaSubscription | AWS::SNS::Subscription |
View the full CloudFormation template
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Generated by TheArchitect — https://www.thecloudarchitect.dev",
"Metadata": {
"TheArchitect": {
"Edges": [
{
"From": "ProcessorRoleiam",
"To": "ProcessorLambdalambda",
"Label": "role"
},
{
"From": "ProcessorLambdalambda",
"To": "EventQueuesqs",
"Label": "enqueue"
},
{
"From": "EventQueuesqs",
"To": "WorkerLambdalambda",
"Label": "trigger"
},
{
"From": "WorkerRoleiam",
"To": "WorkerLambdalambda",
"Label": "role"
},
{
"From": "WorkerLambdalambda",
"To": "EventsTabledynamodb",
"Label": "write"
},
{
"From": "WorkerLambdalambda",
"To": "AlertTopicsns",
"Label": "alerts"
}
]
}
},
"Resources": {
"WAFwaf": {
"Type": "AWS::WAFv2::WebACL",
"Properties": {
"Name": "saa-events-web-acl",
"Scope": "REGIONAL",
"DefaultAction": {
"Allow": {}
},
"VisibilityConfig": {
"CloudWatchMetricsEnabled": true,
"MetricName": "SaaEventsWebACL",
"SampledRequestsEnabled": true
},
"Description": "WAF protecting the API Gateway endpoint."
}
},
"APIGatewayapigateway": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Name": "saa-events-api",
"Description": "Event ingestion REST API.",
"EndpointConfiguration": {
"Types": [
"REGIONAL"
]
},
"MinimumCompressionSize": 1024
}
},
"ProcessorLambdalambda": {
"Type": "AWS::Lambda::Function",
"DependsOn": [
"ProcessorRoleiam"
],
"Properties": {
"Runtime": "nodejs20.x",
"Handler": "index.handler",
"MemorySize": 512,
"Architectures": [
"arm64"
],
"Timeout": 30,
"Description": "Validates and routes incoming events to SQS.",
"Environment": {
"Variables": {
"APP_ENV": "demo",
"LOG_LEVEL": "info"
}
},
"TracingConfig": {
"Mode": "Active"
},
"Code": {
"ZipFile": "exports.handler = async (event) => ({ statusCode: 200, body: JSON.stringify({ ok: true, requestId: event.requestContext?.requestId || 'demo' }) });"
},
"Role": {
"Fn::GetAtt": [
"ProcessorRoleiam",
"Arn"
]
}
}
},
"ProcessorRoleiam": {
"Type": "AWS::IAM::Role",
"Properties": {
"RoleName": "saa-events-processor-role",
"Description": "Execution role for the event processor Lambda.",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"ManagedPolicyArns": [
"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
"arn:aws:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole"
]
}
},
"EventQueuesqs": {
"Type": "AWS::SQS::Queue",
"Properties": {
"QueueName": "saa-events-queue",
"VisibilityTimeout": 120,
"MessageRetentionPeriod": 345600,
"ReceiveMessageWaitTimeSeconds": 20
}
},
"WorkerLambdalambda": {
"Type": "AWS::Lambda::Function",
"DependsOn": [
"WorkerRoleiam"
],
"Properties": {
"Runtime": "nodejs20.x",
"Handler": "index.handler",
"MemorySize": 512,
"Architectures": [
"arm64"
],
"Timeout": 30,
"Description": "Processes queue messages and persists to DynamoDB.",
"Environment": {
"Variables": {
"APP_ENV": "demo",
"LOG_LEVEL": "info"
}
},
"TracingConfig": {
"Mode": "Active"
},
"Code": {
"ZipFile": "exports.handler = async (event) => ({ processed: Array.isArray(event.Records) ? event.Records.length : 1 });"
},
"Role": {
"Fn::GetAtt": [
"WorkerRoleiam",
"Arn"
]
}
}
},
"WorkerRoleiam": {
"Type": "AWS::IAM::Role",
"Properties": {
"RoleName": "saa-events-worker-role",
"Description": "Execution role for the queue worker Lambda.",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"ManagedPolicyArns": [
"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
"arn:aws:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole"
]
}
},
"EventsTabledynamodb": {
"Type": "AWS::DynamoDB::Table",
"Properties": {
"TableName": "saa-events-records",
"BillingMode": "PAY_PER_REQUEST",
"AttributeDefinitions": [
{
"AttributeName": "pk",
"AttributeType": "S"
},
{
"AttributeName": "sk",
"AttributeType": "S"
}
],
"KeySchema": [
{
"AttributeName": "pk",
"KeyType": "HASH"
},
{
"AttributeName": "sk",
"KeyType": "RANGE"
}
],
"PointInTimeRecoverySpecification": {
"PointInTimeRecoveryEnabled": true
},
"SSESpecification": {
"SSEEnabled": true
}
}
},
"AlertTopicsns": {
"Type": "AWS::SNS::Topic",
"Properties": {
"TopicName": "saa-events-alerts",
"DisplayName": "Serverless event alerts"
}
},
"ProcessorLambdalambdaEventQueuesqsEventSourceMapping": {
"Type": "AWS::Lambda::EventSourceMapping",
"DependsOn": [
"ProcessorLambdalambda",
"EventQueuesqs"
],
"Properties": {
"EventSourceArn": {
"Fn::GetAtt": [
"EventQueuesqs",
"Arn"
]
},
"FunctionName": {
"Ref": "ProcessorLambdalambda"
},
"Enabled": true,
"BatchSize": 10
}
},
"WorkerLambdalambdaEventQueuesqsEventSourceMapping": {
"Type": "AWS::Lambda::EventSourceMapping",
"DependsOn": [
"WorkerLambdalambda",
"EventQueuesqs"
],
"Properties": {
"EventSourceArn": {
"Fn::GetAtt": [
"EventQueuesqs",
"Arn"
]
},
"FunctionName": {
"Ref": "WorkerLambdalambda"
},
"Enabled": true,
"BatchSize": 10
}
},
"WorkerLambdalambdaAlertTopicsnsPermission": {
"Type": "AWS::Lambda::Permission",
"DependsOn": [
"WorkerLambdalambda",
"AlertTopicsns"
],
"Properties": {
"Action": "lambda:InvokeFunction",
"FunctionName": {
"Ref": "WorkerLambdalambda"
},
"Principal": "sns.amazonaws.com",
"SourceArn": {
"Ref": "AlertTopicsns"
}
}
},
"AlertTopicsnsWorkerLambdalambdaSubscription": {
"Type": "AWS::SNS::Subscription",
"DependsOn": [
"WorkerLambdalambdaAlertTopicsnsPermission"
],
"Properties": {
"TopicArn": {
"Ref": "AlertTopicsns"
},
"Protocol": "lambda",
"Endpoint": {
"Fn::GetAtt": [
"WorkerLambdalambda",
"Arn"
]
}
}
}
}
}Generated by TheArchitect from the diagram above. Review values such as names, CIDR ranges and instance sizes before deploying to your account.
Related templates
- Twitter/X Scale TestCloudFront behavior split, API Gateway VPC Link, public/internal ALBs, private app/data subnets, queues, media, and data stores
- Step Functions OrchestrationSAA-C03: API Gateway → Step Functions → Lambda tasks + SQS + DynamoDB + SNS
- FIFO Payments LedgerExactly-once payment processing with FIFO SQS deduplication, conditional DynamoDB writes for idempotency, CMK encryption, and a DLQ alert path.