TheArchitect

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.

WAFAPI GatewayProcessor Lamb…Processor RoleEvent QueueWorker LambdaWorker RoleEvents TableAlert Topic
Serverless Event Processing architecture diagram

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 IDResource type
WAFwafAWS::WAFv2::WebACL
APIGatewayapigatewayAWS::ApiGateway::RestApi
ProcessorLambdalambdaAWS::Lambda::Function
ProcessorRoleiamAWS::IAM::Role
EventQueuesqsAWS::SQS::Queue
WorkerLambdalambdaAWS::Lambda::Function
WorkerRoleiamAWS::IAM::Role
EventsTabledynamodbAWS::DynamoDB::Table
AlertTopicsnsAWS::SNS::Topic
ProcessorLambdalambdaEventQueuesqsEventSourceMappingAWS::Lambda::EventSourceMapping
WorkerLambdalambdaEventQueuesqsEventSourceMappingAWS::Lambda::EventSourceMapping
WorkerLambdalambdaAlertTopicsnsPermissionAWS::Lambda::Permission
AlertTopicsnsWorkerLambdalambdaSubscriptionAWS::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