# Common Scenarios Real-world scenarios and solutions for PrismaClient singleton pattern. ## Scenario 1: Converting Existing Codebase **Current state:** Multiple files create their own PrismaClient **Steps:** 1. Create central singleton: `lib/db.ts` ```typescript import { PrismaClient } from '@prisma/client' const globalForPrisma = globalThis as unknown as { prisma: PrismaClient | undefined } export const prisma = globalForPrisma.prisma ?? new PrismaClient() if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma ``` 2. Use Grep to find all `new PrismaClient()` calls: ```bash grep -rn "new PrismaClient()" --include="*.ts" --include="*.js" . ``` 3. Replace with imports from `lib/db.ts`: **Before:** ```typescript import { PrismaClient } from '@prisma/client' const prisma = new PrismaClient() export async function getUsers() { return await prisma.user.findMany() } ``` **After:** ```typescript import { prisma } from '@/lib/db' export async function getUsers() { return await prisma.user.findMany() } ``` 4. Remove old instantiations 5. Validate with grep (should find only one instance): ```bash grep -rn "new PrismaClient()" --include="*.ts" --include="*.js" . | wc -l ``` Expected: `1` --- ## Scenario 2: Next.js Application **Setup:** 1. Create `lib/prisma.ts` with global singleton pattern 2. Import in Server Components: ```typescript import { prisma } from '@/lib/prisma' export default async function UsersPage() { const users = await prisma.user.findMany() return } ``` 3. Import in Server Actions: ```typescript 'use server' import { prisma } from '@/lib/prisma' export async function createUser(formData: FormData) { const email = formData.get('email') as string return await prisma.user.create({ data: { email } }) } ``` 4. Import in Route Handlers: ```typescript import { NextResponse } from 'next/server' import { prisma } from '@/lib/prisma' export async function GET() { const users = await prisma.user.findMany() return NextResponse.json(users) } ``` 5. Set `connection_limit=1` in DATABASE_URL for Vercel: ``` DATABASE_URL="postgresql://user:pass@host:5432/db?connection_limit=1" ``` **Validation:** - Hot reload shouldn't create new connections - No P1017 errors in development - Production deployments handle concurrent requests --- ## Scenario 3: Encountering P1017 Errors **Symptoms:** - "Can't reach database server" errors - "Too many connections" in database logs - Intermittent connection failures - Error code: P1017 **Diagnosis:** 1. Grep codebase for `new PrismaClient()`: ```bash grep -rn "new PrismaClient()" --include="*.ts" --include="*.js" . ``` 2. Check count of instances: ```bash grep -rn "new PrismaClient()" --include="*.ts" --include="*.js" . | wc -l ``` If > 1: Multiple instance problem 3. Review connection pool configuration: ```bash grep -rn "connection_limit" .env* schema.prisma ``` If missing in serverless: Misconfiguration problem **Fix:** 1. Implement singleton pattern (see Scenario 1) 2. Configure connection_limit for serverless: **Development (.env.local):** ``` DATABASE_URL="postgresql://user:pass@host:5432/db?connection_limit=10" ``` **Production (Vercel):** ``` DATABASE_URL="postgresql://user:pass@host:5432/db?connection_limit=1" ``` 3. Monitor connection count after deployment: ```sql SELECT count(*) FROM pg_stat_activity WHERE datname = 'your_database'; ``` Expected: Should stabilize at reasonable number (not growing) --- ## Scenario 4: Multiple Files Creating Clients **Problem:** Different service files create their own clients **Before:** **`services/users.ts`:** ```typescript import { PrismaClient } from '@prisma/client' const prisma = new PrismaClient() export async function getUsers() { return await prisma.user.findMany() } ``` **`services/posts.ts`:** ```typescript import { PrismaClient } from '@prisma/client' const prisma = new PrismaClient() export async function getPosts() { return await prisma.post.findMany() } ``` **Problems:** - Two separate connection pools - Doubled memory usage - Doubled connection count - Multiplies with every service file **After:** **`lib/db.ts`:** ```typescript import { PrismaClient } from '@prisma/client' const prisma = new PrismaClient() export default prisma ``` **`services/users.ts`:** ```typescript import prisma from '@/lib/db' export async function getUsers() { return await prisma.user.findMany() } ``` **`services/posts.ts`:** ```typescript import prisma from '@/lib/db' export async function getPosts() { return await prisma.post.findMany() } ``` **Result:** - Single connection pool shared across services - Reduced memory usage - Stable connection count --- ## Connection Pool Configuration The singleton pattern works with proper pool configuration: **Default pool size:** 10 connections per PrismaClient **Serverless (Vercel, Lambda):** ``` DATABASE_URL="postgresql://user:pass@host/db?connection_limit=1" ``` **Traditional servers:** Calculate: `connection_limit = (num_instances * 2) + 1` - 1 server = 3 connections - 2 servers = 5 connections - 4 servers = 9 connections **Development:** Default (10) is fine since only one developer instance runs. **Example configuration per environment:** ```typescript const connectionLimit = process.env.NODE_ENV === 'production' ? 1 : 10 export const prisma = new PrismaClient({ datasources: { db: { url: process.env.DATABASE_URL + `?connection_limit=${connectionLimit}` } } }) ``` --- ## Why This Matters Real-world impact from stress testing: - **80% of agents** created multiple instances - **100% of those** would fail in production under load - **P1017 errors** in serverless after ~10 concurrent requests - **Memory leaks** from abandoned connection pools - **Database locked out** teams during testing **The singleton pattern prevents all of these issues.** Use this pattern **always**, even if your app is small. It becomes critical as you scale, and retrofitting is painful.