Initial commit
This commit is contained in:
163
skills/configuring-serverless-clients/SKILL.md
Normal file
163
skills/configuring-serverless-clients/SKILL.md
Normal file
@@ -0,0 +1,163 @@
|
||||
---
|
||||
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`
|
||||
Reference in New Issue
Block a user