DEV Community

Thesius Code
Thesius Code

Posted on • Originally published at datanest-stores.pages.dev

Serverless Patterns Collection

Serverless Patterns Collection

A curated library of 30+ serverless architecture patterns with production-ready implementations for AWS Lambda, Azure Functions, and GCP Cloud Functions. Each pattern includes infrastructure-as-code templates, handler code, event source configurations, and cost analysis. From simple API backends to complex event-processing pipelines, choreographed sagas, and fan-out/fan-in workflows — every pattern is documented with when to use it, when NOT to use it, and real-world cost projections at various scales.

Key Features

  • 30+ Patterns Cataloged — Each with architecture diagram, IaC template, handler code, and trade-off analysis
  • Multi-Cloud Implementations — Every pattern available for AWS Lambda, Azure Functions, and GCP Cloud Functions
  • Cost Calculator — Per-pattern cost projections at 1K, 10K, 100K, and 1M daily invocations
  • Cold Start Mitigation — Provisioned concurrency configs, keep-warm strategies, and language-specific optimization tips
  • Event Source Configurations — API Gateway, S3/Blob triggers, queue processors, scheduled cron, and CDC stream handlers
  • Observability Built-In — Structured logging, distributed tracing (X-Ray/App Insights), and custom CloudWatch metrics
  • Error Handling Patterns — DLQ setup, retry policies, idempotency middleware, and circuit breaker wrappers
  • Security Hardened — Least-privilege IAM roles, VPC configurations, and secrets management for each pattern

Quick Start

# Deploy Pattern #1: REST API Backend
cd src/patterns/01-rest-api-backend/aws/
terraform init
terraform apply -var="project_name=acme" -var="environment=staging"

# Test the deployed API
API_URL=$(terraform output -raw api_gateway_url)
curl -X POST "$API_URL/items" \
  -H "Content-Type: application/json" \
  -d '{"name": "Test Item", "price": 29.99}'

# Deploy Pattern #5: S3 Event Processor
cd ../../05-s3-event-processor/aws/
terraform apply -var="project_name=acme"
Enter fullscreen mode Exit fullscreen mode

Architecture

┌──────────────────────────────────────────────────────────┐
│           Serverless Patterns Overview                   │
│                                                          │
│  Pattern 1: REST API         Pattern 2: Async Processor  │
│  ┌──────┐  ┌──────┐ ┌───┐  ┌──────┐ ┌──────┐ ┌──────┐  │
│  │Client│─►│API GW│►│ λ │  │Event │►│Queue │►│  λ   │  │
│  └──────┘  └──────┘ └─┬─┘  │Source│ │(SQS) │ └──┬───┘  │
│                       │     └──────┘ └──────┘    │      │
│                    ┌──▼──┐                    ┌──▼──┐   │
│                    │ DDB │                    │ S3  │   │
│                    └─────┘                    └─────┘   │
│                                                          │
│  Pattern 3: Fan-Out          Pattern 4: Choreography    │
│  ┌──────┐  ┌──────┐         ┌──┐  ┌──┐  ┌──┐           │
│  │  λ   │─►│ SNS  │─►λ─►DB │λA│─►│λB│─►│λC│           │
│  └──────┘  │      │─►λ─►S3 └──┘  └──┘  └──┘           │
│            │      │─►λ─►SQS  via EventBridge            │
│            └──────┘                                      │
│                                                          │
│  Pattern 5: Scheduled        Pattern 6: Stream Proc.    │
│  ┌──────────┐  ┌──┐         ┌────────┐  ┌──┐  ┌─────┐  │
│  │CloudWatch│─►│λ │         │Kinesis/│─►│λ │─►│Store│  │
│  │ Events   │  └──┘         │DDB Str.│  └──┘  └─────┘  │
│  └──────────┘               └────────┘                   │
└──────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Usage Examples

Pattern 1: REST API Backend (Lambda + API Gateway)

# src/patterns/01-rest-api-backend/aws/main.tf
resource "aws_apigatewayv2_api" "main" {
  name          = "${var.project_name}-api"
  protocol_type = "HTTP"
}

resource "aws_lambda_function" "api_handler" {
  function_name = "${var.project_name}-api-handler"
  runtime       = "python3.12"
  handler       = "handler.lambda_handler"
  filename      = data.archive_file.lambda_zip.output_path
  role          = aws_iam_role.lambda_exec.arn
  memory_size   = 256
  timeout       = 30

  environment {
    variables = {
      TABLE_NAME  = aws_dynamodb_table.items.name
      LOG_LEVEL   = "INFO"
      ENVIRONMENT = var.environment
    }
  }

  tracing_config {
    mode = "Active"   # Enable X-Ray tracing
  }
}

resource "aws_lambda_function_event_invoke_config" "api" {
  function_name          = aws_lambda_function.api_handler.function_name
  maximum_retry_attempts = 0   # API calls should not retry
}
Enter fullscreen mode Exit fullscreen mode

Pattern 1: Handler Code

# src/patterns/01-rest-api-backend/aws/handler.py
"""REST API handler with structured logging and error handling."""
import json
import logging
import os
import uuid
from datetime import datetime, timezone

logger = logging.getLogger()
logger.setLevel(os.environ.get("LOG_LEVEL", "INFO"))

def lambda_handler(event: dict, context) -> dict:
    """Route API Gateway requests to appropriate handlers."""
    method = event.get("requestContext", {}).get("http", {}).get("method")
    path = event.get("rawPath", "")

    logger.info("Request: %s %s", method, path)

    try:
        match (method, path):
            case ("GET", "/items"):
                return list_items()
            case ("POST", "/items"):
                body = json.loads(event.get("body", "{}"))
                return create_item(body)
            case ("GET", path) if path.startswith("/items/"):
                item_id = path.split("/")[-1]
                return get_item(item_id)
            case _:
                return response(404, {"error": "Not found"})
    except json.JSONDecodeError:
        return response(400, {"error": "Invalid JSON body"})
    except Exception as e:
        logger.exception("Unhandled error")
        return response(500, {"error": "Internal server error"})

def create_item(body: dict) -> dict:
    """Create a new item in DynamoDB."""
    import boto3
    table = boto3.resource("dynamodb").Table(os.environ["TABLE_NAME"])
    item = {
        "id": str(uuid.uuid4()),
        "name": body["name"],
        "price": str(body["price"]),
        "created_at": datetime.now(timezone.utc).isoformat(),
    }
    table.put_item(Item=item)
    return response(201, item)

def response(status: int, body: dict) -> dict:
    """Format API Gateway response."""
    return {
        "statusCode": status,
        "headers": {"Content-Type": "application/json"},
        "body": json.dumps(body, default=str),
    }
Enter fullscreen mode Exit fullscreen mode

Pattern 5: S3 Event Processor

# src/patterns/05-s3-event-processor/aws/template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Resources:
  ProcessorFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: processor.handler
      Runtime: python3.12
      MemorySize: 512
      Timeout: 300
      Policies:
        - S3ReadPolicy:
            BucketName: !Ref UploadBucket
        - DynamoDBCrudPolicy:
            TableName: !Ref MetadataTable
      Events:
        S3Upload:
          Type: S3
          Properties:
            Bucket: !Ref UploadBucket
            Events: s3:ObjectCreated:*
            Filter:
              S3Key:
                Rules:
                  - Name: suffix
                    Value: .csv
      DeadLetterQueue:
        Type: SQS
        TargetArn: !GetAtt ProcessorDLQ.Arn
Enter fullscreen mode Exit fullscreen mode

Configuration

# configs/serverless-defaults.yaml
runtime: python3.12
default_memory_mb: 256              # Start small, optimize later
default_timeout_seconds: 30         # API: 30s, async: 300s, batch: 900s

cold_start_mitigation:
  provisioned_concurrency: 5        # For latency-sensitive APIs
  keep_warm_schedule: "rate(5 minutes)"  # For low-traffic functions

error_handling:
  max_retry_attempts: 2             # For async invocations
  dlq_retention_days: 14
  enable_idempotency: true          # Use event ID as idempotency key

observability:
  log_level: INFO
  tracing: xray                     # xray, none
  custom_metrics: true              # Publish business metrics to CloudWatch

cost_controls:
  max_concurrent_executions: 100    # Account-level safety limit
  reserved_concurrency: 50          # Per-function limit for critical functions
Enter fullscreen mode Exit fullscreen mode

Best Practices

  • Set concurrency limits — One runaway function can exhaust your account's concurrency pool and starve other functions
  • Use async invocation for non-interactive workloads — Queue-triggered Lambda handles retries and DLQ automatically
  • Minimize cold starts for APIs — Use provisioned concurrency for customer-facing endpoints; accept cold starts for batch jobs
  • Keep functions focused — One function per responsibility; a 2,000-line Lambda is a monolith with extra steps
  • Externalize configuration — Use environment variables or Parameter Store, not hardcoded values
  • Design for idempotency — Every function will be invoked at least once; duplicates are a certainty, not an edge case

Troubleshooting

Issue Cause Fix
Lambda times out at 3 seconds Default timeout too short for workload Increase Timeout in function config (max 900s)
AccessDeniedException on DynamoDB Lambda role missing table permissions Add dynamodb:PutItem / dynamodb:GetItem to execution role
Cold start > 5 seconds Large deployment package or VPC-attached function Reduce package size; use provisioned concurrency for VPC functions
S3 trigger fires twice S3 eventual consistency can send duplicate events Implement idempotency using the S3 object key + version as dedup key

This is 1 of 11 resources in the Cloud Architecture Pro toolkit. Get the complete [Serverless Patterns Collection] with all files, templates, and documentation for $39.

Get the Full Kit →

Or grab the entire Cloud Architecture Pro bundle (11 products) for $149 — save 30%.

Get the Complete Bundle →


Related Articles

Top comments (0)