6.4 KiB
name, description, allowed-tools
| name | description | allowed-tools |
|---|---|---|
| configuring-serverless-clients | Configure PrismaClient for serverless (Next.js, Lambda, Vercel) with connection_limit=1 and global singleton pattern. | Read, Write, Edit, Glob, Grep |
Serverless PrismaClient Configuration
Configure PrismaClient for serverless platforms to prevent connection pool exhaustion using connection limits and global singleton patterns.
Activation Triggers
Deploy to Next.js (App/Pages Router), AWS Lambda, Vercel, or similar serverless platforms; encountering P1017 errors; files in app/, pages/api/, lambda/ directories.
Problem & Solution
Problem: Each Lambda instance creates its own connection pool. Default unlimited pool × instances exhausts database (e.g., 10 instances × 10 connections = 100 connections).
Solution: Set connection_limit=1 in DATABASE_URL; use global singleton to reuse client across invocations; consider PgBouncer for high concurrency.
Configuration Workflow
Phase 1—Environment: Add connection_limit=1 and pool_timeout=20 to DATABASE_URL in .env; configure in Vercel dashboard if applicable.
Phase 2—Client Singleton: Create single global PrismaClient in lib/prisma.ts; use globalThis pattern (Next.js 13+ App Router), global.prisma pattern (Pages Router/Lambda), or initialize outside handler (Lambda standalone).
Phase 3—Validation: Verify DATABASE_URL contains connection parameters; confirm new PrismaClient() appears only in lib/prisma.ts; test with 10+ concurrent requests; monitor connection count.
Implementation Patterns
Next.js App Router (lib/prisma.ts)
import { PrismaClient } from '@prisma/client';
const prismaClientSingleton = () => new PrismaClient();
declare const globalThis: {
prismaGlobal: ReturnType<typeof prismaClientSingleton>;
} & typeof global;
const prisma = globalThis.prismaGlobal ?? prismaClientSingleton();
export default prisma;
if (process.env.NODE_ENV !== 'production') globalThis.prismaGlobal = prisma;
Environment: DATABASE_URL="postgresql://user:pass@host:5432/db?connection_limit=1&pool_timeout=20"
Usage in Server Action:
import prisma from '@/lib/prisma';
export async function createUser(formData: FormData) {
'use server';
return await prisma.user.create({
data: {
email: formData.get('email') as string,
name: formData.get('name') as string,
},
});
}
Next.js Pages Router / AWS Lambda (lib/prisma.ts)
import { PrismaClient } from '@prisma/client';
declare global {
var prisma: PrismaClient | undefined;
}
const prisma = global.prisma || new PrismaClient();
if (process.env.NODE_ENV !== 'production') global.prisma = prisma;
export default prisma;
Lambda Handler:
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
export const handler = async (event: any) => {
const users = await prisma.user.findMany();
return { statusCode: 200, body: JSON.stringify(users) };
// Never call prisma.$disconnect() – reuse Lambda container connections
};
With PgBouncer (High Concurrency)
Use when >50
concurrent requests or connection limits still cause issues.
DATABASE_URL="postgresql://user:pass@pgbouncer-host:6543/db?connection_limit=10&pool_timeout=20"
DIRECT_URL="postgresql://user:pass@direct-host:5432/db"
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
directUrl = env("DIRECT_URL")
}
DATABASE_URL → PgBouncer (queries); DIRECT_URL → database (migrations).
Requirements
MUST: Set connection_limit=1 in DATABASE_URL; use global singleton (never new PrismaClient() in functions); create single lib/prisma.ts imported throughout; add pool_timeout.
SHOULD: Use PgBouncer for high concurrency (>50); monitor production connection count; set limit via URL parameter; reuse Lambda connections.
NEVER: Create new PrismaClient() in routes/handlers; use default pool size in serverless; call $disconnect() in handlers; deploy without limits; split instances across files.
Common Mistakes
| Issue | Wrong | Right |
|---|---|---|
| Connection in Constructor | new PrismaClient({ datasources: { db: { url: ... } } }) |
Set connection_limit=1 in DATABASE_URL |
| Multiple Instances | const prisma = new PrismaClient() inside functions |
Import singleton from lib/prisma |
| Handler Disconnect | await prisma.$disconnect() after query |
Remove disconnect – reuse containers |
| Missing TypeScript Declaration | const prisma = global.prisma || ... |
declare global { var prisma: ... } first |
Validation Checklist
- Environment: DATABASE_URL contains
connection_limit=1andpool_timeout; verified in Vercel dashboard. - Code:
new PrismaClient()appears exactly once inlib/prisma.ts; all other files import from it. - Testing: 10+ concurrent requests to staging; connection count stays ≤10-15; no P1017 errors.
- Monitoring: Production alerts for connection exhaustion and timeout errors.
Platform-Specific Notes
Vercel: Set environment variables in dashboard (
auto-encrypted); connection pooling shared across invocations; consider Vercel Postgres for built-in pooling.
AWS Lambda: Container reuse varies by traffic; cold starts create new instances; use provisioned concurrency for consistency; Lambda layers optimize Prisma binary size.
Cloudflare Workers: Standard PrismaClient unsupported (V8 isolates, not Node.js); use Data Proxy or D1.
Railway/Render: Apply connection_limit=1 pattern; check platform docs for built-in pooling.
Related Skills
Next.js Integration:
- If implementing authenticated data access layers in Next.js, use the securing-data-access-layer skill from nextjs-16 for verifySession() DAL patterns
- If securing server actions with database operations, use the securing-server-actions skill from nextjs-16 for authentication patterns
References
- Next.js patterns:
references/nextjs-patterns.md - Lambda optimization:
references/lambda-patterns.md - PgBouncer setup:
references/pgbouncer-guide.md