Loading...
Loading...
AWS serverless and event-driven architecture expert based on Well-Architected Framework. Use when building serverless APIs, Lambda functions, REST APIs, microservices, or async workflows. Covers Lambda with TypeScript/Python, API Gateway (REST/HTTP), DynamoDB, Step Functions, EventBridge, SQS, SNS, and serverless patterns. Essential when user mentions serverless, Lambda, API Gateway, event-driven, async processing, queues, pub/sub, or wants to build scalable serverless applications with AWS best practices.
npx skill4agent add zxkane/aws-skills aws-serverless-edamcp__aws-mcp__aws___search_documentationmcp__*awsdocs*__aws___search_documentationmcp__aws-mcp__aws___read_documentationmcp__*awsdocs*__aws___read_documentationmcp__aws-mcp__aws___get_regional_availabilityaws-mcp-setup// ✅ GOOD - Single purpose, focused function
export const processOrder = async (event: OrderEvent) => {
// Only handles order processing
const order = await validateOrder(event);
await saveOrder(order);
await publishOrderCreatedEvent(order);
return { statusCode: 200, body: JSON.stringify({ orderId: order.id }) };
};
// ❌ BAD - Function does too much
export const handleEverything = async (event: any) => {
// Handles orders, inventory, payments, shipping...
// Too many responsibilities
};// Consider concurrent Lambda executions accessing DynamoDB
const table = new dynamodb.Table(this, 'Table', {
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST, // Auto-scales with load
});
// Or with provisioned capacity + auto-scaling
const table = new dynamodb.Table(this, 'Table', {
billingMode: dynamodb.BillingMode.PROVISIONED,
readCapacity: 5,
writeCapacity: 5,
});
// Enable auto-scaling for concurrent load
table.autoScaleReadCapacity({ minCapacity: 5, maxCapacity: 100 });
table.autoScaleWriteCapacity({ minCapacity: 5, maxCapacity: 100 });// ❌ BAD - Relying on local file system
export const handler = async (event: any) => {
fs.writeFileSync('/tmp/data.json', JSON.stringify(data)); // Lost after execution
};
// ✅ GOOD - Use persistent storage
export const handler = async (event: any) => {
await s3.putObject({
Bucket: process.env.BUCKET_NAME,
Key: 'data.json',
Body: JSON.stringify(data),
});
};// ❌ BAD - Lambda function chaining
export const handler1 = async (event: any) => {
const result = await processStep1(event);
await lambda.invoke({
FunctionName: 'handler2',
Payload: JSON.stringify(result),
});
};
// ✅ GOOD - Step Functions orchestration
const stateMachine = new stepfunctions.StateMachine(this, 'OrderWorkflow', {
definition: stepfunctions.Chain
.start(validateOrder)
.next(processPayment)
.next(shipOrder)
.next(sendConfirmation),
});// Pattern: Event-driven processing
const bucket = new s3.Bucket(this, 'DataBucket');
bucket.addEventNotification(
s3.EventType.OBJECT_CREATED,
new s3n.LambdaDestination(processFunction),
{ prefix: 'uploads/' }
);
// Pattern: EventBridge integration
const rule = new events.Rule(this, 'OrderRule', {
eventPattern: {
source: ['orders'],
detailType: ['OrderPlaced'],
},
});
rule.addTarget(new targets.LambdaFunction(processOrderFunction));// ✅ GOOD - Idempotent operation
export const handler = async (event: SQSEvent) => {
for (const record of event.Records) {
const orderId = JSON.parse(record.body).orderId;
// Check if already processed (idempotency)
const existing = await dynamodb.getItem({
TableName: process.env.TABLE_NAME,
Key: { orderId },
});
if (existing.Item) {
console.log('Order already processed:', orderId);
continue; // Skip duplicate
}
// Process order
await processOrder(orderId);
// Mark as processed
await dynamodb.putItem({
TableName: process.env.TABLE_NAME,
Item: { orderId, processedAt: Date.now() },
});
}
};async function withRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000));
}
}
throw new Error('Max retries exceeded');
}// Create custom event bus
const eventBus = new events.EventBus(this, 'AppEventBus', {
eventBusName: 'application-events',
});
// Define event schema
const schema = new events.Schema(this, 'OrderSchema', {
schemaName: 'OrderPlaced',
definition: events.SchemaDefinition.fromInline({
openapi: '3.0.0',
info: { version: '1.0.0', title: 'Order Events' },
paths: {},
components: {
schemas: {
OrderPlaced: {
type: 'object',
properties: {
orderId: { type: 'string' },
customerId: { type: 'string' },
amount: { type: 'number' },
},
},
},
},
}),
});
// Create rules for different consumers
new events.Rule(this, 'ProcessOrderRule', {
eventBus,
eventPattern: {
source: ['orders'],
detailType: ['OrderPlaced'],
},
targets: [new targets.LambdaFunction(processOrderFunction)],
});
new events.Rule(this, 'NotifyCustomerRule', {
eventBus,
eventPattern: {
source: ['orders'],
detailType: ['OrderPlaced'],
},
targets: [new targets.LambdaFunction(notifyCustomerFunction)],
});// Standard queue for at-least-once delivery
const queue = new sqs.Queue(this, 'ProcessingQueue', {
visibilityTimeout: Duration.seconds(300),
retentionPeriod: Duration.days(14),
deadLetterQueue: {
queue: dlq,
maxReceiveCount: 3,
},
});
// FIFO queue for ordered processing
const fifoQueue = new sqs.Queue(this, 'OrderedQueue', {
fifo: true,
contentBasedDeduplication: true,
deduplicationScope: sqs.DeduplicationScope.MESSAGE_GROUP,
});
// Lambda consumer
new lambda.EventSourceMapping(this, 'QueueConsumer', {
target: processingFunction,
eventSourceArn: queue.queueArn,
batchSize: 10,
maxBatchingWindow: Duration.seconds(5),
});// Create SNS topic
const topic = new sns.Topic(this, 'OrderTopic', {
displayName: 'Order Events',
});
// Multiple SQS queues subscribe to topic
const inventoryQueue = new sqs.Queue(this, 'InventoryQueue');
const shippingQueue = new sqs.Queue(this, 'ShippingQueue');
const analyticsQueue = new sqs.Queue(this, 'AnalyticsQueue');
topic.addSubscription(new subscriptions.SqsSubscription(inventoryQueue));
topic.addSubscription(new subscriptions.SqsSubscription(shippingQueue));
topic.addSubscription(new subscriptions.SqsSubscription(analyticsQueue));
// Each queue has its own Lambda consumer
new lambda.EventSourceMapping(this, 'InventoryConsumer', {
target: inventoryFunction,
eventSourceArn: inventoryQueue.queueArn,
});const reserveFlight = new tasks.LambdaInvoke(this, 'ReserveFlight', {
lambdaFunction: reserveFlightFunction,
outputPath: '$.Payload',
});
const reserveHotel = new tasks.LambdaInvoke(this, 'ReserveHotel', {
lambdaFunction: reserveHotelFunction,
outputPath: '$.Payload',
});
const processPayment = new tasks.LambdaInvoke(this, 'ProcessPayment', {
lambdaFunction: processPaymentFunction,
outputPath: '$.Payload',
});
// Compensating transactions
const cancelFlight = new tasks.LambdaInvoke(this, 'CancelFlight', {
lambdaFunction: cancelFlightFunction,
});
const cancelHotel = new tasks.LambdaInvoke(this, 'CancelHotel', {
lambdaFunction: cancelHotelFunction,
});
// Define saga with compensation
const definition = reserveFlight
.next(reserveHotel)
.next(processPayment)
.addCatch(cancelHotel.next(cancelFlight), {
resultPath: '$.error',
});
new stepfunctions.StateMachine(this, 'BookingStateMachine', {
definition,
timeout: Duration.minutes(5),
});// Event store with DynamoDB
const eventStore = new dynamodb.Table(this, 'EventStore', {
partitionKey: { name: 'aggregateId', type: dynamodb.AttributeType.STRING },
sortKey: { name: 'version', type: dynamodb.AttributeType.NUMBER },
stream: dynamodb.StreamViewType.NEW_IMAGE,
});
// Lambda function stores events
export const handleCommand = async (event: any) => {
const { aggregateId, eventType, eventData } = event;
// Get current version
const items = await dynamodb.query({
TableName: process.env.EVENT_STORE,
KeyConditionExpression: 'aggregateId = :id',
ExpressionAttributeValues: { ':id': aggregateId },
ScanIndexForward: false,
Limit: 1,
});
const nextVersion = items.Items?.[0]?.version + 1 || 1;
// Append new event
await dynamodb.putItem({
TableName: process.env.EVENT_STORE,
Item: {
aggregateId,
version: nextVersion,
eventType,
eventData,
timestamp: Date.now(),
},
});
};
// Projections read from event stream
eventStore.grantStreamRead(projectionFunction);const api = new apigateway.RestApi(this, 'Api', {
restApiName: 'microservices-api',
deployOptions: {
throttlingRateLimit: 1000,
throttlingBurstLimit: 2000,
tracingEnabled: true,
},
});
// User service
const users = api.root.addResource('users');
users.addMethod('GET', new apigateway.LambdaIntegration(getUsersFunction));
users.addMethod('POST', new apigateway.LambdaIntegration(createUserFunction));
// Order service
const orders = api.root.addResource('orders');
orders.addMethod('GET', new apigateway.LambdaIntegration(getOrdersFunction));
orders.addMethod('POST', new apigateway.LambdaIntegration(createOrderFunction));const stream = new kinesis.Stream(this, 'DataStream', {
shardCount: 2,
retentionPeriod: Duration.days(7),
});
// Lambda processes stream records
new lambda.EventSourceMapping(this, 'StreamProcessor', {
target: processFunction,
eventSourceArn: stream.streamArn,
batchSize: 100,
maxBatchingWindow: Duration.seconds(5),
parallelizationFactor: 10,
startingPosition: lambda.StartingPosition.LATEST,
retryAttempts: 3,
bisectBatchOnError: true,
onFailure: new lambdaDestinations.SqsDestination(dlq),
});// SQS queue for tasks
const taskQueue = new sqs.Queue(this, 'TaskQueue', {
visibilityTimeout: Duration.minutes(5),
receiveMessageWaitTime: Duration.seconds(20), // Long polling
deadLetterQueue: {
queue: dlq,
maxReceiveCount: 3,
},
});
// Lambda worker processes tasks
const worker = new lambda.Function(this, 'TaskWorker', {
// ... configuration
reservedConcurrentExecutions: 10, // Control concurrency
});
new lambda.EventSourceMapping(this, 'TaskConsumer', {
target: worker,
eventSourceArn: taskQueue.queueArn,
batchSize: 10,
reportBatchItemFailures: true, // Partial batch failure handling
});// Daily cleanup job
new events.Rule(this, 'DailyCleanup', {
schedule: events.Schedule.cron({ hour: '2', minute: '0' }),
targets: [new targets.LambdaFunction(cleanupFunction)],
});
// Process every 5 minutes
new events.Rule(this, 'FrequentProcessing', {
schedule: events.Schedule.rate(Duration.minutes(5)),
targets: [new targets.LambdaFunction(processFunction)],
});// API Gateway endpoint for webhooks
const webhookApi = new apigateway.RestApi(this, 'WebhookApi', {
restApiName: 'webhooks',
});
const webhook = webhookApi.root.addResource('webhook');
webhook.addMethod('POST', new apigateway.LambdaIntegration(webhookFunction, {
proxy: true,
timeout: Duration.seconds(29), // API Gateway max
}));
// Lambda handler validates and queues webhook
export const handler = async (event: APIGatewayProxyEvent) => {
// Validate webhook signature
const isValid = validateSignature(event.headers, event.body);
if (!isValid) {
return { statusCode: 401, body: 'Invalid signature' };
}
// Queue for async processing
await sqs.sendMessage({
QueueUrl: process.env.QUEUE_URL,
MessageBody: event.body,
});
// Return immediately
return { statusCode: 202, body: 'Accepted' };
};export const handler = async (event: SQSEvent) => {
const failures: SQSBatchItemFailure[] = [];
for (const record of event.Records) {
try {
await processRecord(record);
} catch (error) {
console.error('Failed to process record:', record.messageId, error);
failures.push({ itemIdentifier: record.messageId });
}
}
// Return partial batch failures for retry
return { batchItemFailures: failures };
};const dlq = new sqs.Queue(this, 'DLQ', {
retentionPeriod: Duration.days(14),
});
const queue = new sqs.Queue(this, 'Queue', {
deadLetterQueue: {
queue: dlq,
maxReceiveCount: 3,
},
});
// Monitor DLQ depth
new cloudwatch.Alarm(this, 'DLQAlarm', {
metric: dlq.metricApproximateNumberOfMessagesVisible(),
threshold: 1,
evaluationPeriods: 1,
alarmDescription: 'Messages in DLQ require attention',
});new NodejsFunction(this, 'Function', {
entry: 'src/handler.ts',
tracing: lambda.Tracing.ACTIVE, // X-Ray tracing
environment: {
POWERTOOLS_SERVICE_NAME: 'order-service',
POWERTOOLS_METRICS_NAMESPACE: 'MyApp',
LOG_LEVEL: 'INFO',
},
});references/serverless-patterns.mdreferences/eda-patterns.mdreferences/security-best-practices.mdreferences/observability-best-practices.mdreferences/performance-optimization.mdreferences/deployment-best-practices.md