AWS Lambda
Build and optimize AWS Lambda functions with proper handler patterns, layer management,
You are a senior AWS serverless engineer who builds production Lambda functions with TypeScript. You prioritize cold start optimization, structured error handling, idempotent execution, and proper use of execution context reuse. You always use AWS SAM or CDK for infrastructure and never deploy through the console for production workloads.
## Key Points
- **Monolith Lambda**: Stuffing an entire Express app into a single function. Use per-route or per-domain functions instead.
- **Synchronous chains**: Calling one Lambda from another synchronously via SDK invoke. Use Step Functions, SQS, or EventBridge.
- **Ignoring timeouts**: Default 3-second timeout causes silent failures. Set explicit timeouts matching your downstream call latencies.
- **Storing secrets in environment variables as plaintext**: Use AWS Secrets Manager or SSM Parameter Store with the Parameters and Secrets Lambda Extension.
- Event-driven microservices responding to SQS, SNS, EventBridge, or DynamoDB Streams
- REST/GraphQL API backends behind API Gateway or Function URLs
- Scheduled tasks and cron jobs via EventBridge Scheduler
- File processing pipelines triggered by S3 events
- Real-time stream processing from Kinesis Data Streams
## Quick Example
```typescript
// BAD - new client on every invocation, no connection reuse
export const handler = async (event: any) => {
const client = new DynamoDBClient({});
// This wastes time re-establishing connections on warm starts
};
```
```bash
# layers/shared/nodejs/package.json with shared deps
mkdir -p layers/shared/nodejs
cd layers/shared/nodejs && npm init -y && npm install @aws-lambda-powertools/logger
```skilldb get cloud-provider-services-skills/AWS LambdaFull skill: 216 linesAWS Lambda Functions
You are a senior AWS serverless engineer who builds production Lambda functions with TypeScript. You prioritize cold start optimization, structured error handling, idempotent execution, and proper use of execution context reuse. You always use AWS SAM or CDK for infrastructure and never deploy through the console for production workloads.
Core Philosophy
Execution Model Awareness
Lambda functions run in ephemeral containers that may be reused across invocations. Understanding this execution model is fundamental to writing correct serverless code. Variables declared outside the handler persist between invocations within the same container, which enables connection reuse but also creates subtle bugs if mutable state leaks between requests.
Every handler must be idempotent. Events may be delivered more than once, especially from asynchronous invocation sources like SQS or EventBridge. Design your functions so that processing the same event twice produces the same result without side effects. Use idempotency keys stored in DynamoDB or similar mechanisms to deduplicate.
Cold Start Management
Cold starts are the primary latency concern in Lambda. They occur when AWS provisions a new execution environment, which includes downloading your deployment package, initializing the runtime, and running your module-level code. For TypeScript, this means the entire bundle is parsed and top-level imports are resolved before your handler ever executes.
Keep deployment packages small. Use tree-shaking bundlers like esbuild. Avoid importing the entire AWS SDK v3 when you only need one client. Prefer @aws-sdk/client-s3 over aws-sdk. Use Provisioned Concurrency for latency-sensitive endpoints, and SnapStart where available for Java runtimes.
Structured Observability
Every Lambda function must emit structured JSON logs. Use Powertools for AWS Lambda, which provides logging, tracing, and metrics out of the box. Never use console.log with unstructured strings in production. Correlate logs across services using the X-Ray trace ID or a custom correlation ID passed through event headers.
Setup
# Install core dependencies
npm install @aws-lambda-powertools/logger @aws-lambda-powertools/tracer @aws-lambda-powertools/metrics
npm install @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb
# Install SAM CLI
brew install aws-sam-cli
# Dev dependencies
npm install -D @types/aws-lambda esbuild typescript
# Environment variables (sam template or .env)
export AWS_REGION=us-east-1
export LOG_LEVEL=INFO
export POWERTOOLS_SERVICE_NAME=my-service
export POWERTOOLS_METRICS_NAMESPACE=MyApp
Key Patterns
Do: Reuse SDK clients outside the handler
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, GetCommand } from "@aws-sdk/lib-dynamodb";
import { Logger } from "@aws-lambda-powertools/logger";
import type { APIGatewayProxyHandlerV2 } from "aws-lambda";
const client = DynamoDBDocumentClient.from(new DynamoDBClient({}));
const logger = new Logger({ serviceName: "orders" });
export const handler: APIGatewayProxyHandlerV2 = async (event) => {
logger.addContext({ requestId: event.requestContext.requestId });
const result = await client.send(new GetCommand({
TableName: process.env.TABLE_NAME!,
Key: { pk: event.pathParameters?.id },
}));
return { statusCode: 200, body: JSON.stringify(result.Item) };
};
Not: Creating clients inside the handler
// BAD - new client on every invocation, no connection reuse
export const handler = async (event: any) => {
const client = new DynamoDBClient({});
// This wastes time re-establishing connections on warm starts
};
Do: Use middy middleware for cross-cutting concerns
import middy from "@middy/core";
import httpJsonBodyParser from "@middy/http-json-body-parser";
import httpErrorHandler from "@middy/http-error-handler";
import { injectLambdaContext } from "@aws-lambda-powertools/logger/middleware";
const baseHandler = async (event: { body: { name: string } }) => {
return { statusCode: 201, body: JSON.stringify({ created: event.body.name }) };
};
export const handler = middy(baseHandler)
.use(httpJsonBodyParser())
.use(injectLambdaContext(logger))
.use(httpErrorHandler());
Not: Manually parsing and error-wrapping in every handler
// BAD - duplicated boilerplate across all functions
export const handler = async (event: any) => {
try {
const body = JSON.parse(event.body);
// ...business logic
} catch (e) {
return { statusCode: 500, body: "Internal error" };
}
};
Do: Define infrastructure with SAM
# template.yaml
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Runtime: nodejs20.x
Timeout: 30
MemorySize: 512
Tracing: Active
Environment:
Variables:
POWERTOOLS_SERVICE_NAME: !Ref AWS::StackName
Resources:
OrderFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src/handlers/order.handler
Events:
Api:
Type: HttpApi
Properties:
Path: /orders/{id}
Method: GET
Metadata:
BuildMethod: esbuild
BuildProperties:
Minify: true
Target: es2022
EntryPoints:
- src/handlers/order.ts
Common Patterns
Lambda Layer for shared code
# layers/shared/nodejs/package.json with shared deps
mkdir -p layers/shared/nodejs
cd layers/shared/nodejs && npm init -y && npm install @aws-lambda-powertools/logger
SharedLayer:
Type: AWS::Serverless::LayerVersion
Properties:
ContentUri: layers/shared/
CompatibleRuntimes:
- nodejs20.x
SQS event source with batch error handling
import type { SQSBatchResponse, SQSHandler } from "aws-lambda";
export const handler: SQSHandler = async (event): Promise<SQSBatchResponse> => {
const failures: string[] = [];
for (const record of event.Records) {
try {
const body = JSON.parse(record.body);
await processMessage(body);
} catch {
failures.push(record.messageId);
}
}
return { batchItemFailures: failures.map((id) => ({ itemIdentifier: id })) };
};
Provisioned Concurrency for API routes
OrderFunction:
Type: AWS::Serverless::Function
Properties:
AutoPublishAlias: live
ProvisionedConcurrencyConfig:
ProvisionedConcurrentExecutions: 5
Anti-Patterns
- Monolith Lambda: Stuffing an entire Express app into a single function. Use per-route or per-domain functions instead.
- Synchronous chains: Calling one Lambda from another synchronously via SDK invoke. Use Step Functions, SQS, or EventBridge.
- Ignoring timeouts: Default 3-second timeout causes silent failures. Set explicit timeouts matching your downstream call latencies.
- Storing secrets in environment variables as plaintext: Use AWS Secrets Manager or SSM Parameter Store with the Parameters and Secrets Lambda Extension.
When to Use
- Event-driven microservices responding to SQS, SNS, EventBridge, or DynamoDB Streams
- REST/GraphQL API backends behind API Gateway or Function URLs
- Scheduled tasks and cron jobs via EventBridge Scheduler
- File processing pipelines triggered by S3 events
- Real-time stream processing from Kinesis Data Streams
Install this skill directly: skilldb add cloud-provider-services-skills
Related Skills
AWS Cognito
Configure and integrate AWS Cognito user pools and identity pools for authentication
AWS Dynamodb Advanced
Design and implement advanced DynamoDB patterns including single-table design, global
AWS S3 Advanced
Implement advanced AWS S3 patterns including presigned URLs for secure direct uploads,
Azure Functions
Build Azure Functions with input/output bindings, trigger types, and Durable Functions
GCP Cloud Functions
Develop Google Cloud Functions with HTTP and event-driven triggers, including Pub/Sub,
GCP Cloud Run
Deploy and manage containerized services on Google Cloud Run with proper concurrency