Introduction
Lambda cold starts slow response times, PostgreSQL connections get exhausted with every invocation — serverless has unique design patterns. Generate designs with Claude Code.
CLAUDE.md Serverless Rules
## Serverless Design Rules
### Cold Start Mitigation
- Initialize outside the handler (DB connections, config loading)
- Provisioned Concurrency for critical endpoints
- Minimize bundle size (exclude unused dependencies)
### DB Connections
- Don't use connection pools (Lambda = 1 request = 1 instance)
- Connect to PostgreSQL via RDS Proxy (connection limit management)
- Lambda execution environments are reused, but new instances also spin up
### Function Design
- 1 function = 1 responsibility (don't pack routing in)
- Set timeout to minimum needed (default 3s can be too short)
- Design for idempotency (assume retries will happen)
### Vercel Edge Functions
- Node.js APIs not available (Edge Runtime)
- Keep responses lightweight (target under 50ms)
- Specialize in pure computation like JWT verification
Generate Lambda Function
Design a user registration API for AWS Lambda.
Requirements:
- Cold start mitigation (initialize outside handler)
- PostgreSQL connection via RDS Proxy
- Validation with Zod
- Return errors with appropriate HTTP status codes
Generate file: functions/createUser/handler.ts
Generated Lambda Function
// functions/createUser/handler.ts
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
import { PrismaClient } from '@prisma/client';
import { z } from 'zod';
// Initialize outside handler (skipped when Lambda instance is reused)
const prisma = new PrismaClient({
datasources: {
db: { url: process.env.DATABASE_URL },
// For RDS Proxy: ?connection_limit=1&pool_timeout=0
},
});
const createUserSchema = z.object({
email: z.string().email(),
name: z.string().min(1).max(100),
password: z.string().min(8),
});
// Lambda handler
export const handler = async (
event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
const requestId = event.requestContext?.requestId ?? crypto.randomUUID();
try {
const body = JSON.parse(event.body ?? '{}');
const result = createUserSchema.safeParse(body);
if (!result.success) {
return {
statusCode: 422,
headers: { 'Content-Type': 'application/json', 'X-Request-ID': requestId },
body: JSON.stringify({
errors: result.error.errors.map(e => ({
field: e.path.join('.'),
message: e.message,
})),
}),
};
}
const { email, name, password } = result.data;
// Duplicate check
const existing = await prisma.user.findUnique({ where: { email } });
if (existing) {
return {
statusCode: 409,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ error: 'Email already registered' }),
};
}
const hashedPassword = await bcrypt.hash(password, 12);
const user = await prisma.user.create({
data: { email, name, passwordHash: hashedPassword },
select: { id: true, email: true, name: true, createdAt: true },
});
return {
statusCode: 201,
headers: { 'Content-Type': 'application/json', 'X-Request-ID': requestId },
body: JSON.stringify(user),
};
} catch (err) {
console.error({ err, requestId }, 'Unexpected error');
return {
statusCode: 500,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ error: 'Internal server error' }),
};
}
};
Vercel Edge Functions (JWT Verification)
// middleware.ts (Vercel Edge Runtime)
import { NextRequest, NextResponse } from 'next/server';
import { jwtVerify } from 'jose'; // JWT implementation without Node.js API
const JWT_SECRET = new TextEncoder().encode(process.env.JWT_SECRET!);
export async function middleware(request: NextRequest) {
const token = request.cookies.get('access_token')?.value;
if (!token) {
return NextResponse.redirect(new URL('/login', request.url));
}
try {
const { payload } = await jwtVerify(token, JWT_SECRET);
// Pass verified user info via headers to backend
const response = NextResponse.next();
response.headers.set('X-User-Id', payload.userId as string);
response.headers.set('X-Tenant-Id', payload.tenantId as string);
return response;
} catch (err) {
// Invalid JWT → redirect to login
return NextResponse.redirect(new URL('/login', request.url));
}
}
export const config = {
matcher: ['/dashboard/:path*', '/api/protected/:path*'],
};
Summary
Design Serverless with Claude Code:
- CLAUDE.md — document cold start mitigation, DB connection policy, Edge constraints
- Initialize outside handler — reduce initialization cost on Lambda instance reuse
- RDS Proxy — prevent DB connection explosion from Lambda
-
Vercel Edge — don't use Node.js APIs (use Edge-compatible libraries like
jose)
Review serverless designs with **Code Review Pack (¥980)* using /code-review at prompt-works.jp*
myouga (@myougatheaxo) — Axolotl VTuber.
Top comments (0)