| 1 | Overview | 2 | Architecture Overview |
| 3 | AWS Service Topology | 4 | Implementation Guide |
| 5 | Decision Criteria | 6 | Cost Model |
| 7 | Anti-Patterns to Avoid | 8 | References |
Overview
The serverless pattern on AWS inverts the traditional deployment model. Instead of running a server continuously and routing requests to it, you deploy a function and AWS runs it only when triggered. The function scales automatically, runs in parallel for concurrent requests, and costs nothing when idle. This model is ideal for APIs with variable traffic, event processors, scheduled jobs, and workflow orchestration.
The key architectural decision in serverless is choosing between Lambda for single-operation functions and Step Functions for multi-step workflows requiring state, branching, and error handling across steps.
Architecture Overview
%%{init:{'theme':'base','themeVariables':{'fontSize':'14px','fontFamily':'IBM Plex Sans, system-ui, sans-serif','primaryColor':'#DBEAFE','primaryTextColor':'#1e3a5f','primaryBorderColor':'#2563EB','lineColor':'#374151','clusterBkg':'#F9FAFB','clusterBorder':'#D1D5DB','edgeLabelBackground':'#FFFFFF'},'flowchart':{'curve':'orthogonal','padding':30,'nodeSpacing':65,'rankSpacing':75,'useMaxWidth':true}}}%% flowchart TD START([Client Request or Event Trigger]) START --> TRIGGER{Trigger Type} TRIGGER -->|HTTP request| APIGW[API Gateway\nHTTP API or REST API\nAuthoriser, throttling, CORS] TRIGGER -->|Schedule| SCHED[EventBridge Scheduler\nCron or rate expression\nRetry with DLQ] TRIGGER -->|Queue message| SQS_T[SQS Queue\nBatch processing\nDead letter queue] TRIGGER -->|Multi-step workflow| SFN[Step Functions\nState machine\nError handling per step] APIGW --> LAMBDA[AWS Lambda Function\nStateless execution\nVPC or public network] SCHED --> LAMBDA SQS_T --> LAMBDA SFN --> LAMBDA LAMBDA --> DECISION{Response Type} DECISION -->|Synchronous| RESP[Return response\nto API Gateway] DECISION -->|Async continuation| NEXT[Publish event\nto EventBridge or SQS] DECISION -->|Workflow step| SFN RESP --> END([Response Returned to Client]) NEXT --> END style START fill:#4f8ef7,color:#fff style END fill:#10b981,color:#fff style SFN fill:#e0f2fe style LAMBDA fill:#e0f2fe
AWS Service Topology
%%{init:{'theme':'base','themeVariables':{'fontSize':'14px','fontFamily':'IBM Plex Sans, system-ui, sans-serif','primaryColor':'#DBEAFE','primaryTextColor':'#1e3a5f','primaryBorderColor':'#2563EB','lineColor':'#374151','clusterBkg':'#F9FAFB','clusterBorder':'#D1D5DB','edgeLabelBackground':'#FFFFFF'},'flowchart':{'curve':'orthogonal','padding':30,'nodeSpacing':65,'rankSpacing':75,'useMaxWidth':true}}}%% flowchart TD CLIENT[Client Application\nWeb, Mobile, or Service] subgraph GATEWAY["API Gateway — HTTP API preferred or REST API"] AUTH[Lambda Authoriser\nJWT validation\nCustom business logic] THROTTLE[Usage Plans\nPer-client rate limiting\n10,000 RPS default limit] STAGE[Deployment Stages\ndev, staging, production\nCanary deployments] end subgraph FUNCTIONS["AWS Lambda"] FN_API[API Handler Functions\nNode.js 20 or Python 3.12\nArm64 Graviton2 runtime] FN_WARM[Provisioned Concurrency\nPre-warmed instances\nFor latency-sensitive paths] LAYER[Lambda Layers\nShared dependencies\nSDK and utility code] end subgraph WORKFLOW["AWS Step Functions — Express or Standard"] SFN_DEF[State Machine Definition\nAmazon States Language\nVisual workflow editor] SFN_ERR[Error Handling\nRetry with backoff\nCatch and compensate] end subgraph STORAGE["Data Layer"] DDB_SL[DynamoDB\nSingle-table design\nOn-demand billing] CACHE[ElastiCache Serverless\nRedis compatible\nPay per ECU used] S3_SL[Amazon S3\nLarge object storage\nPresigned URL pattern] end CLIENT --> AUTH --> THROTTLE --> STAGE STAGE --> FN_API FN_API --> LAYER FN_WARM -.->|Pre-warmed| FN_API FN_API --> DDB_SL & CACHE & S3_SL FN_API -->|Long workflow| SFN_DEF SFN_DEF --> SFN_ERR SFN_DEF --> FN_API style CLIENT fill:#4f8ef7,color:#fff style FN_WARM fill:#e0f2fe style SFN_ERR fill:#fef3c7
Implementation Guide
CDK Stack — Serverless REST API
import * as cdk from 'aws-cdk-lib';
import * as apigwv2 from 'aws-cdk-lib/aws-apigatewayv2';
import * as integrations from 'aws-cdk-lib/aws-apigatewayv2-integrations';
import * as authorizers from 'aws-cdk-lib/aws-apigatewayv2-authorizers';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import * as logs from 'aws-cdk-lib/aws-logs';
import { Duration, RemovalPolicy } from 'aws-cdk-lib';
export class ServerlessApiStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Single-table DynamoDB design
const table = new dynamodb.Table(this, 'AppTable', {
partitionKey: { name: 'PK', type: dynamodb.AttributeType.STRING },
sortKey: { name: 'SK', type: dynamodb.AttributeType.STRING },
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
pointInTimeRecovery: true,
removalPolicy: RemovalPolicy.RETAIN,
});
// Lambda authoriser — JWT validation
const authFunction = new lambda.Function(this, 'JwtAuthoriser', {
runtime: lambda.Runtime.NODEJS_20_X,
architecture: lambda.Architecture.ARM_64,
handler: 'index.handler',
code: lambda.Code.fromAsset('lambda/authoriser'),
environment: { JWKS_URI: process.env.JWKS_URI ?? '' },
timeout: Duration.seconds(5),
});
const authorizer = new authorizers.HttpLambdaAuthorizer(
'JwtAuthorizer', authFunction, {
responseTypes: [authorizers.HttpLambdaResponseType.SIMPLE],
resultsCacheTtl: Duration.minutes(5),
});
// API handler — Arm64 Graviton2 for 20% cost reduction
const apiHandler = new lambda.Function(this, 'ApiHandler', {
runtime: lambda.Runtime.NODEJS_20_X,
architecture: lambda.Architecture.ARM_64,
handler: 'index.handler',
code: lambda.Code.fromAsset('lambda/api-handler'),
environment: { TABLE_NAME: table.tableName },
timeout: Duration.seconds(29),
logRetention: logs.RetentionDays.ONE_MONTH,
});
table.grantReadWriteData(apiHandler);
// HTTP API — lower cost than REST API
const api = new apigwv2.HttpApi(this, 'AppApi', {
corsPreflight: {
allowOrigins: ['https://ascendion.engineering'],
allowMethods: [apigwv2.CorsHttpMethod.GET,
apigwv2.CorsHttpMethod.POST,
apigwv2.CorsHttpMethod.PUT,
apigwv2.CorsHttpMethod.DELETE],
allowHeaders: ['Authorization', 'Content-Type'],
},
defaultAuthorizer: authorizer,
});
api.addRoutes({
path: '/api/{proxy+}',
methods: [apigwv2.HttpMethod.ANY],
integration: new integrations.HttpLambdaIntegration(
'ApiIntegration', apiHandler),
});
}
}
Step Functions — Order Fulfilment Workflow
import * as sfn from 'aws-cdk-lib/aws-stepfunctions';
import * as tasks from 'aws-cdk-lib/aws-stepfunctions-tasks';
const validateOrder = new tasks.LambdaInvoke(this, 'ValidateOrder', {
lambdaFunction: validateFn,
outputPath: '$.Payload',
});
const processPayment = new tasks.LambdaInvoke(this, 'ProcessPayment', {
lambdaFunction: paymentFn,
outputPath: '$.Payload',
retryOnServiceExceptions: true,
});
const notifyCustomer = new tasks.LambdaInvoke(this, 'NotifyCustomer', {
lambdaFunction: notifyFn,
});
const compensate = new tasks.LambdaInvoke(this, 'CompensateOrder', {
lambdaFunction: compensateFn,
comment: 'Reverse any completed steps on failure',
});
const orderFailed = new sfn.Fail(this, 'OrderFailed', {
error: 'OrderProcessingFailed',
});
const definition = validateOrder
.next(processPayment
.addCatch(compensate.next(orderFailed), {
errors: ['PaymentDeclined', 'PaymentTimeout'],
}))
.next(notifyCustomer);
new sfn.StateMachine(this, 'OrderWorkflow', {
definition,
stateMachineType: sfn.StateMachineType.EXPRESS,
timeout: Duration.minutes(5),
tracingEnabled: true,
});
Decision Criteria
| Use Case | Recommended | Alternative | Reason to Prefer Lambda |
|---|---|---|---|
| REST API endpoint | Lambda + API Gateway HTTP API | ECS Fargate | No cold start concern for APIs with consistent traffic |
| Scheduled batch job | Lambda + EventBridge Scheduler | ECS scheduled task | Lambda free tier covers most batch jobs |
| Multi-step workflow | Step Functions + Lambda | Lambda orchestrating Lambda | Step Functions externalises state, retries, error handling |
| Long-running job over 15min | ECS Fargate | Lambda | Lambda hard limit is 15 minutes |
| Persistent WebSocket | API Gateway WebSocket API | ALB + ECS | Managed connection management |
| High-frequency tight loop | Lambda with Provisioned Concurrency | ECS Fargate | Remove cold start for latency-sensitive paths |
Cost Model
| Service | Free Tier | Pay-as-you-go |
|---|---|---|
| Lambda | 1M requests/month, 400K GB-seconds | $0.20/million requests + $0.0000166/GB-second |
| API Gateway HTTP API | 1M calls/month first 12 months | $1.00/million calls |
| Step Functions Express | None | $1.00/million state transitions |
| DynamoDB | 25GB storage, 25 WCU, 25 RCU | On-demand from $0.25/million reads |
A typical serverless API handling 10M requests per month at 200ms average duration costs approximately $8–15 per month including DynamoDB.
Anti-Patterns to Avoid
One Lambda function with a single handler routing all API endpoints internally with a router library (Express.js inside Lambda). The function grows to hundreds of lines, all code is deployed on every change, and a single cold start loads everything even for the simplest endpoint.
One Lambda function per logical operation or route group. Each function is small, independently deployable, and scales based on its own traffic pattern.
Function A invokes Function B via SDK and waits for the response before returning. If B is slow, A times out and the caller retries, amplifying load on B. Error handling becomes a chain of nested try-catch across function boundaries.
Use Step Functions to orchestrate multi-function workflows. Externalise state and error handling to the state machine rather than encoding it in Lambda code.
Storing database passwords, API keys, and tokens as Lambda environment variables. They appear in plain text in the Lambda console, CloudTrail, and any log that captures environment state.
Store secrets in AWS Secrets Manager. Fetch at initialisation time outside the handler (warm cache), reference by ARN in the environment variable. Rotate automatically with Secrets Manager rotation.
Flowchart
References
- AWS — Lambda Developer Guide. docs.aws.amazon.com/lambda
- AWS — API Gateway HTTP API vs REST API comparison. docs.aws.amazon.com/apigateway
- AWS — Step Functions Developer Guide. docs.aws.amazon.com/step-functions
- AWS — Serverless Application Model (SAM). docs.aws.amazon.com/serverless-application-model
- Sbarski, Peter — Serverless Architectures on AWS. Manning, 2017.