Files
2025-11-29 18:22:25 +08:00

164 lines
6.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: configuring-serverless-clients
description: Configure PrismaClient for serverless (Next.js, Lambda, Vercel) with connection_limit=1 and global singleton pattern.
allowed-tools: 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`)
```typescript
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:**
```typescript
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`)
```typescript
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:**
```typescript
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.
```bash
DATABASE_URL="postgresql://user:pass@pgbouncer-host:6543/db?connection_limit=10&pool_timeout=20"
DIRECT_URL="postgresql://user:pass@direct-host:5432/db"
```
```prisma
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
1. **Environment:** DATABASE_URL contains `connection_limit=1` and `pool_timeout`; verified in Vercel dashboard.
2. **Code:** `new PrismaClient()` appears exactly once in `lib/prisma.ts`; all other files import from it.
3. **Testing:** 10+ concurrent requests to staging; connection count stays ≤10-15; no P1017 errors.
4. **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`