Serverless architecture enables building scalable applications without managing infrastructure.
Vercel Edge Functions
// app/api/edge/route.ts
import { NextRequest, NextResponse } from 'next/server';
export const runtime = 'edge';
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const name = searchParams.get('name') || 'World';
return NextResponse.json({
message: `Hello, ${name}!`,
timestamp: new Date().toISOString(),
region: process.env.VERCEL_REGION,
});
}
AWS Lambda with API Gateway
// lambda/handler.ts
import { APIGatewayProxyHandler } from 'aws-lambda';
export const handler: APIGatewayProxyHandler = async (event) => {
const { name = 'World' } = event.queryStringParameters || {};
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({
message: `Hello, ${name}!`,
timestamp: new Date().toISOString(),
}),
};
};
Serverless Framework
# serverless.yml
service: my-service
provider:
name: aws
runtime: nodejs20.x
region: us-east-1
environment:
DATABASE_URL: ${env:DATABASE_URL}
functions:
hello:
handler: handler.hello
events:
- httpApi:
path: /hello
method: get
createUser:
handler: users.create
events:
- httpApi:
path: /users
method: post
environment:
TABLE_NAME: !Ref UsersTable
processQueue:
handler: queue.process
events:
- sqs:
arn: !GetAtt ProcessQueue.Arn
batchSize: 10
resources:
Resources:
UsersTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: users
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
ProcessQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: process-queue
DynamoDB Operations
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import {
DynamoDBDocumentClient,
PutCommand,
GetCommand,
QueryCommand,
UpdateCommand,
} from '@aws-sdk/lib-dynamodb';
const client = new DynamoDBClient({});
const docClient = DynamoDBDocumentClient.from(client);
// Create item
export async function createUser(user: User) {
await docClient.send(
new PutCommand({
TableName: 'users',
Item: {
id: crypto.randomUUID(),
...user,
createdAt: new Date().toISOString(),
},
})
);
}
// Get item
export async function getUser(id: string) {
const result = await docClient.send(
new GetCommand({
TableName: 'users',
Key: { id },
})
);
return result.Item;
}
// Query with index
export async function getUsersByEmail(email: string) {
const result = await docClient.send(
new QueryCommand({
TableName: 'users',
IndexName: 'email-index',
KeyConditionExpression: 'email = :email',
ExpressionAttributeValues: {
':email': email,
},
})
);
return result.Items;
}
S3 File Upload
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
const s3Client = new S3Client({});
export async function getUploadUrl(fileName: string) {
const command = new PutObjectCommand({
Bucket: process.env.BUCKET_NAME,
Key: `uploads/${Date.now()}-${fileName}`,
ContentType: 'image/jpeg',
});
const url = await getSignedUrl(s3Client, command, { expiresIn: 3600 });
return url;
}
// Client-side upload
async function uploadFile(file: File) {
const url = await fetch('/api/upload-url?name=' + file.name).then((r) =>
r.json()
);
await fetch(url, {
method: 'PUT',
body: file,
headers: {
'Content-Type': file.type,
},
});
}
SQS Queue Processing
import { SQSHandler } from 'aws-lambda';
export const handler: SQSHandler = async (event) => {
for (const record of event.Records) {
const message = JSON.parse(record.body);
try {
await processMessage(message);
console.log('Processed message:', message.id);
} catch (error) {
console.error('Failed to process:', error);
throw error; // Message will be retried
}
}
};
async function processMessage(message: any) {
// Process the message
await sendEmail(message.email, message.content);
}
Step Functions State Machine
{
"Comment": "Order processing workflow",
"StartAt": "ValidateOrder",
"States": {
"ValidateOrder": {
"Type": "Task",
"Resource": "arn:aws:lambda:us-east-1:123456789:function:validate-order",
"Next": "ProcessPayment",
"Catch": [
{
"ErrorEquals": ["ValidationError"],
"Next": "OrderFailed"
}
]
},
"ProcessPayment": {
"Type": "Task",
"Resource": "arn:aws:lambda:us-east-1:123456789:function:process-payment",
"Next": "FulfillOrder",
"Retry": [
{
"ErrorEquals": ["PaymentError"],
"MaxAttempts": 3,
"BackoffRate": 2
}
]
},
"FulfillOrder": {
"Type": "Task",
"Resource": "arn:aws:lambda:us-east-1:123456789:function:fulfill-order",
"Next": "OrderComplete"
},
"OrderComplete": {
"Type": "Succeed"
},
"OrderFailed": {
"Type": "Fail",
"Error": "OrderProcessingFailed"
}
}
}
Cold Start Optimization
// Initialization outside handler
import { PrismaClient } from '@prisma/client';
let prisma: PrismaClient;
export const handler = async (event) => {
// Reuse connection across invocations
if (!prisma) {
prisma = new PrismaClient();
}
const users = await prisma.user.findMany();
return { statusCode: 200, body: JSON.stringify(users) };
};
Event-Driven Architecture
// EventBridge pattern
import {
EventBridgeClient,
PutEventsCommand,
} from '@aws-sdk/client-eventbridge';
const client = new EventBridgeClient({});
export async function publishEvent(type: string, data: any) {
await client.send(
new PutEventsCommand({
Entries: [
{
Source: 'my-app',
DetailType: type,
Detail: JSON.stringify(data),
EventBusName: 'default',
},
],
})
);
}
// Listener Lambda
export const handler = async (event: any) => {
const { 'detail-type': type, detail } = event;
if (type === 'UserCreated') {
await sendWelcomeEmail(detail.email);
}
};
Serverless architecture provides automatic scaling, pay-per-use pricing, and reduced operational overhead.
Posted ondevopswith tags: