Difficulty: Intermediate
What is serverless computing? Explain AWS Lambda, its advantages and limitations, and when to use it.
Serverless computing lets you run code without provisioning or managing servers. The cloud provider handles all infrastructure: scaling, patching, availability. You pay only for actual compute time, not idle servers.
AWS Lambda is the most popular serverless platform. You upload a function, define a trigger (HTTP request, S3 upload, scheduled event, queue message), and Lambda runs your code when triggered. It scales automatically from zero to thousands of concurrent executions.
Advantages: - Zero server management and patching - Automatic scaling (including to zero when idle) - Pay-per-use billing (per 1ms of execution) - Built-in high availability across multiple zones
Limitations: - Cold starts: first invocation after idle period is slower (100ms-2s) - Execution timeout: maximum 15 minutes per invocation - Stateless: no local state between invocations - Vendor lock-in: code depends on cloud provider APIs - Debugging is harder than traditional servers
Ideal for: APIs with variable traffic, event processing (image resizing, email sending), scheduled tasks (cron jobs), and webhooks. Not ideal for: long-running processes, WebSocket connections, or applications with consistent high traffic (cheaper to run a server).
// handler.ts - AWS Lambda function
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
export const handler = async (
event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
try {
const body = JSON.parse(event.body || '{}');
const { name } = body;
// Your business logic here
const result = await processRequest(name);
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({ message: `Hello, ${name}!`, data: result }),
};
} catch (error) {
console.error('Error:', error);
return {
statusCode: 500,
body: JSON.stringify({ error: 'Internal server error' }),
};
}
};
// S3 trigger example - resize uploaded images
export const imageResizer = async (event: S3Event) => {
for (const record of event.Records) {
const bucket = record.s3.bucket.name;
const key = record.s3.object.key;
// Download image, resize, upload thumbnail
await resizeImage(bucket, key);
}
};
Lambda functions receive an event object and return a response. API Gateway events include HTTP method, headers, body, and path parameters. S3 events trigger on file uploads.
# serverless.yml - Infrastructure as Code
service: myapp-api
provider:
name: aws
runtime: nodejs20.x
region: us-east-1
memorySize: 256
timeout: 10
environment:
DATABASE_URL: ${ssm:/myapp/prod/database-url}
JWT_SECRET: ${ssm:/myapp/prod/jwt-secret}
functions:
getUsers:
handler: src/handlers/users.getAll
events:
- http:
path: /api/users
method: get
cors: true
createUser:
handler: src/handlers/users.create
events:
- http:
path: /api/users
method: post
cors: true
processImage:
handler: src/handlers/images.resize
events:
- s3:
bucket: my-uploads
event: s3:ObjectCreated:*
rules:
- suffix: .jpg
dailyReport:
handler: src/handlers/reports.generate
events:
- schedule: cron(0 9 * * ? *) # 9 AM daily
# Deploy
# npx serverless deploy
# npx serverless deploy --stage production
The Serverless Framework defines Lambda functions, their triggers, and infrastructure in YAML. One command deploys everything: functions, API Gateway, S3 triggers, and scheduled events.
// Cold start happens when Lambda creates a new execution environment
// Strategies to minimize impact:
// 1. Keep functions warm with scheduled pings
// serverless.yml:
// functions:
// api:
// handler: handler.main
// events:
// - schedule: rate(5 minutes) # ping every 5 min
// 2. Minimize bundle size (faster initialization)
// Use esbuild or webpack to tree-shake
// Avoid large SDKs - import only what you need:
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
// NOT: import AWS from 'aws-sdk'; // imports everything
// 3. Initialize outside the handler (reused between invocations)
import { PrismaClient } from '@prisma/client';
// This runs ONCE when the container starts (cold start)
const prisma = new PrismaClient();
// This runs on EVERY invocation (reuses the connection)
export const handler = async (event: APIGatewayProxyEvent) => {
const users = await prisma.user.findMany();
return { statusCode: 200, body: JSON.stringify(users) };
};
// 4. Provisioned Concurrency (costs money but eliminates cold starts)
// aws lambda put-provisioned-concurrency-config \
// --function-name myFunction \
// --qualifier prod \
// --provisioned-concurrent-executions 5
// Cold start times (approximate):
// Node.js: ~200-500ms
// Python: ~200-500ms
// Java: ~3-10 seconds
// Go/Rust: ~50-100ms
Initialize expensive resources (DB connections, SDK clients) outside the handler so they persist between invocations. Use the v3 AWS SDK for smaller bundles. Provisioned Concurrency eliminates cold starts.
Serverless, AWS Lambda, API Gateway, Cold Start, Event-Driven, FaaS