Initial commit
This commit is contained in:
310
skills/creating-client-singletons/references/common-scenarios.md
Normal file
310
skills/creating-client-singletons/references/common-scenarios.md
Normal file
@@ -0,0 +1,310 @@
|
||||
# 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 <UserList users={users} />
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,238 @@
|
||||
# Serverless Pattern
|
||||
|
||||
Serverless environments require special handling due to cold starts, connection pooling, and function lifecycle constraints.
|
||||
|
||||
## Next.js App Router (Vercel)
|
||||
|
||||
**File: `lib/prisma.ts`**
|
||||
|
||||
```typescript
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
|
||||
const globalForPrisma = globalThis as unknown as {
|
||||
prisma: PrismaClient | undefined
|
||||
}
|
||||
|
||||
export const prisma = globalForPrisma.prisma ?? new PrismaClient({
|
||||
log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
|
||||
})
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
|
||||
```
|
||||
|
||||
**Environment Configuration (.env):**
|
||||
|
||||
```
|
||||
DATABASE_URL="postgresql://user:pass@host:5432/db?connection_limit=1&pool_timeout=10"
|
||||
```
|
||||
|
||||
**Why connection_limit=1:**
|
||||
|
||||
- Each serverless function instance gets ONE connection
|
||||
- Multiple function instances = multiple connections
|
||||
- Prevents pool exhaustion with many concurrent requests
|
||||
- Vercel scales to hundreds of instances automatically
|
||||
|
||||
**Usage in Server Components:**
|
||||
|
||||
```typescript
|
||||
import { prisma } from '@/lib/prisma'
|
||||
|
||||
export default async function UsersPage() {
|
||||
const users = await prisma.user.findMany()
|
||||
return <UserList users={users} />
|
||||
}
|
||||
```
|
||||
|
||||
**Usage 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 }
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
**Usage 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)
|
||||
}
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
|
||||
- Never create PrismaClient in component files
|
||||
- Import singleton from `lib/prisma.ts`
|
||||
- Global pattern survives hot reload
|
||||
- Connection limit prevents pool exhaustion
|
||||
|
||||
---
|
||||
|
||||
## AWS Lambda
|
||||
|
||||
**File: `lib/db.ts`**
|
||||
|
||||
```typescript
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
|
||||
let prisma: PrismaClient
|
||||
|
||||
if (!global.prisma) {
|
||||
global.prisma = new PrismaClient({
|
||||
log: ['error', 'warn']
|
||||
})
|
||||
}
|
||||
|
||||
prisma = global.prisma
|
||||
|
||||
export default prisma
|
||||
```
|
||||
|
||||
**Lambda Handler:**
|
||||
|
||||
```typescript
|
||||
import prisma from './lib/db'
|
||||
|
||||
export async function handler(event: any) {
|
||||
const users = await prisma.user.findMany()
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
body: JSON.stringify(users)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Environment Variables (Lambda):**
|
||||
|
||||
```
|
||||
DATABASE_URL=postgresql://user:pass@host:5432/db?connection_limit=1&pool_timeout=10&connect_timeout=10
|
||||
```
|
||||
|
||||
**Lambda-Specific Considerations:**
|
||||
|
||||
- Lambda reuses container for warm starts
|
||||
- Global singleton survives across invocations
|
||||
- First invocation creates client (cold start)
|
||||
- Subsequent invocations reuse client (warm starts)
|
||||
- No need to disconnect (Lambda freezes container)
|
||||
|
||||
---
|
||||
|
||||
## Connection Pool Calculation for Serverless
|
||||
|
||||
**Formula:**
|
||||
|
||||
```
|
||||
max_connections = (max_concurrent_functions * connection_limit) + buffer
|
||||
```
|
||||
|
||||
**Example (Vercel):**
|
||||
|
||||
- Max concurrent functions: 100
|
||||
- Connection limit per function: 1
|
||||
- Buffer: 10
|
||||
|
||||
**Result:** Need 110 database connections
|
||||
|
||||
**Recommended DATABASE_URL for Vercel:**
|
||||
|
||||
```
|
||||
postgresql://user:pass@host:5432/db?connection_limit=1&pool_timeout=10
|
||||
```
|
||||
|
||||
**Why pool_timeout=10:**
|
||||
|
||||
- Prevents long waits for connections
|
||||
- Fails fast if pool exhausted
|
||||
- User gets error instead of timeout
|
||||
|
||||
---
|
||||
|
||||
## Anti-Pattern: Multiple Files Creating Clients
|
||||
|
||||
**WRONG - Each file creates its own:**
|
||||
|
||||
**`app/api/users/route.ts`:**
|
||||
|
||||
```typescript
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
export async function GET() {
|
||||
return Response.json(await prisma.user.findMany())
|
||||
}
|
||||
```
|
||||
|
||||
**`app/api/posts/route.ts`:**
|
||||
|
||||
```typescript
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
export async function GET() {
|
||||
return Response.json(await prisma.post.findMany())
|
||||
}
|
||||
```
|
||||
|
||||
**Problems in Serverless:**
|
||||
|
||||
- Each route = separate client = separate pool
|
||||
- 2 routes × 50 function instances × 10 connections = 1000 connections!
|
||||
- Database exhausted under load
|
||||
- P1017 errors inevitable
|
||||
|
||||
**Fix - Central singleton:**
|
||||
|
||||
**`lib/prisma.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
|
||||
```
|
||||
|
||||
**`app/api/users/route.ts`:**
|
||||
|
||||
```typescript
|
||||
import { prisma } from '@/lib/prisma'
|
||||
|
||||
export async function GET() {
|
||||
return Response.json(await prisma.user.findMany())
|
||||
}
|
||||
```
|
||||
|
||||
**`app/api/posts/route.ts`:**
|
||||
|
||||
```typescript
|
||||
import { prisma } from '@/lib/prisma'
|
||||
|
||||
export async function GET() {
|
||||
return Response.json(await prisma.post.findMany())
|
||||
}
|
||||
```
|
||||
|
||||
**Result:**
|
||||
|
||||
- 50 function instances × 1 connection = 50 connections
|
||||
- Sustainable and scalable
|
||||
- No P1017 errors
|
||||
259
skills/creating-client-singletons/references/test-pattern.md
Normal file
259
skills/creating-client-singletons/references/test-pattern.md
Normal file
@@ -0,0 +1,259 @@
|
||||
# Test Pattern
|
||||
|
||||
Proper test setup with PrismaClient singleton ensures test isolation and prevents connection exhaustion.
|
||||
|
||||
## Test File Setup
|
||||
|
||||
**Import singleton, don't create:**
|
||||
|
||||
```typescript
|
||||
import { prisma } from '@/lib/prisma'
|
||||
|
||||
describe('User operations', () => {
|
||||
beforeEach(async () => {
|
||||
await prisma.user.deleteMany()
|
||||
})
|
||||
|
||||
it('creates user', async () => {
|
||||
const user = await prisma.user.create({
|
||||
data: { email: 'test@example.com' }
|
||||
})
|
||||
expect(user.email).toBe('test@example.com')
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await prisma.$disconnect()
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
|
||||
- Import singleton, don't create
|
||||
- Clean state with `deleteMany` or transactions
|
||||
- Disconnect once at end of suite
|
||||
- Don't disconnect between tests (kills connection pool)
|
||||
|
||||
---
|
||||
|
||||
## Test Isolation with Transactions
|
||||
|
||||
**Better approach for test isolation:**
|
||||
|
||||
```typescript
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
|
||||
describe('User operations', () => {
|
||||
let testPrisma: Omit<PrismaClient, '$connect' | '$disconnect' | '$on' | '$transaction' | '$use'>
|
||||
|
||||
beforeEach(async () => {
|
||||
await prisma.$transaction(async (tx) => {
|
||||
testPrisma = tx
|
||||
await tx.user.deleteMany()
|
||||
})
|
||||
})
|
||||
|
||||
it('creates user', async () => {
|
||||
const user = await testPrisma.user.create({
|
||||
data: { email: 'test@example.com' }
|
||||
})
|
||||
expect(user.email).toBe('test@example.com')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
**Why this works:**
|
||||
|
||||
- Each test runs in transaction
|
||||
- Automatic rollback after test
|
||||
- No data leakage between tests
|
||||
- Faster than deleteMany
|
||||
|
||||
---
|
||||
|
||||
## Mocking PrismaClient for Unit Tests
|
||||
|
||||
**When to mock:**
|
||||
|
||||
- Testing business logic without database
|
||||
- Fast unit tests
|
||||
- CI/CD pipeline optimization
|
||||
|
||||
**File: `__mocks__/prisma.ts`**
|
||||
|
||||
```typescript
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
import { mockDeep, mockReset, DeepMockProxy } from 'jest-mock-extended'
|
||||
|
||||
export const prismaMock = mockDeep<PrismaClient>()
|
||||
|
||||
beforeEach(() => {
|
||||
mockReset(prismaMock)
|
||||
})
|
||||
```
|
||||
|
||||
**File: `__tests__/userService.test.ts`**
|
||||
|
||||
```typescript
|
||||
import { prismaMock } from '../__mocks__/prisma'
|
||||
import { createUser } from '../services/userService'
|
||||
|
||||
jest.mock('@/lib/prisma', () => ({
|
||||
__esModule: true,
|
||||
default: prismaMock,
|
||||
}))
|
||||
|
||||
describe('User Service', () => {
|
||||
it('creates user with email', async () => {
|
||||
const mockUser = { id: '1', email: 'test@example.com' }
|
||||
|
||||
prismaMock.user.create.mockResolvedValue(mockUser)
|
||||
|
||||
const user = await createUser('test@example.com')
|
||||
|
||||
expect(user.email).toBe('test@example.com')
|
||||
expect(prismaMock.user.create).toHaveBeenCalledWith({
|
||||
data: { email: 'test@example.com' }
|
||||
})
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
|
||||
- Mock the singleton module, not PrismaClient
|
||||
- Reset mocks between tests
|
||||
- Type-safe mocks with jest-mock-extended
|
||||
- Fast tests without database
|
||||
|
||||
---
|
||||
|
||||
## Integration Test Setup
|
||||
|
||||
**File: `tests/setup.ts`**
|
||||
|
||||
```typescript
|
||||
import { prisma } from '@/lib/prisma'
|
||||
|
||||
beforeAll(async () => {
|
||||
await prisma.$connect()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await prisma.$disconnect()
|
||||
})
|
||||
|
||||
export async function cleanDatabase() {
|
||||
const tables = ['User', 'Post', 'Comment']
|
||||
|
||||
for (const table of tables) {
|
||||
await prisma[table.toLowerCase()].deleteMany()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**File: `tests/users.integration.test.ts`**
|
||||
|
||||
```typescript
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { cleanDatabase } from './setup'
|
||||
|
||||
describe('User Integration Tests', () => {
|
||||
beforeEach(async () => {
|
||||
await cleanDatabase()
|
||||
})
|
||||
|
||||
it('creates and retrieves user', async () => {
|
||||
const created = await prisma.user.create({
|
||||
data: { email: 'test@example.com' }
|
||||
})
|
||||
|
||||
const retrieved = await prisma.user.findUnique({
|
||||
where: { id: created.id }
|
||||
})
|
||||
|
||||
expect(retrieved?.email).toBe('test@example.com')
|
||||
})
|
||||
|
||||
it('handles unique constraint', async () => {
|
||||
await prisma.user.create({
|
||||
data: { email: 'test@example.com' }
|
||||
})
|
||||
|
||||
await expect(
|
||||
prisma.user.create({
|
||||
data: { email: 'test@example.com' }
|
||||
})
|
||||
).rejects.toThrow(/Unique constraint/)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
|
||||
- Shared setup in `tests/setup.ts`
|
||||
- Clean database between tests
|
||||
- Test real database behavior
|
||||
- Catch constraint violations
|
||||
|
||||
---
|
||||
|
||||
## Anti-Pattern: Creating Client in Tests
|
||||
|
||||
**WRONG:**
|
||||
|
||||
```typescript
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
|
||||
describe('User tests', () => {
|
||||
let prisma: PrismaClient
|
||||
|
||||
beforeEach(() => {
|
||||
prisma = new PrismaClient()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await prisma.$disconnect()
|
||||
})
|
||||
|
||||
it('creates user', async () => {
|
||||
const user = await prisma.user.create({
|
||||
data: { email: 'test@example.com' }
|
||||
})
|
||||
expect(user.email).toBe('test@example.com')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
**Problems:**
|
||||
|
||||
- New connection pool every test
|
||||
- Connect/disconnect overhead
|
||||
- Connection exhaustion in large suites
|
||||
- Slow tests
|
||||
|
||||
**Fix:**
|
||||
|
||||
```typescript
|
||||
import { prisma } from '@/lib/prisma'
|
||||
|
||||
describe('User tests', () => {
|
||||
beforeEach(async () => {
|
||||
await prisma.user.deleteMany()
|
||||
})
|
||||
|
||||
it('creates user', async () => {
|
||||
const user = await prisma.user.create({
|
||||
data: { email: 'test@example.com' }
|
||||
})
|
||||
expect(user.email).toBe('test@example.com')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
**Result:**
|
||||
|
||||
- Reuses singleton connection
|
||||
- Fast tests
|
||||
- No connection exhaustion
|
||||
Reference in New Issue
Block a user