managing-client-lifecycle
npx skills add https://github.com/djankies/claude-configs --skill managing-client-lifecycle
Agent 安装分布
Skill 文档
PrismaClient Lifecycle Management
Teaches proper PrismaClient lifecycle patterns for connection cleanup and logging following Prisma 6 best practices.
Activates when: Setting up shutdown handlers (SIGINT, SIGTERM), configuring PrismaClient logging, implementing connection cleanup in servers/serverless/tests, writing test teardown logic, or user mentions “shutdown”, “disconnect”, “cleanup”, “logging”, “graceful exit”.
Why it matters: Proper lifecycle management ensures clean connection closure on shutdown, prevents hanging connections from exhausting database resources, provides development/production visibility through logging, and prevents connection leaks in tests.
Implementation Patterns
Long-Running Servers (Express, Fastify, Custom HTTP)
import express from 'express'
import { prisma } from './lib/prisma'
const app = express()
const server = app.listen(3000)
async function gracefulShutdown(signal: string) {
console.log(`Received ${signal}, closing gracefully...`)
server.close(async () => {
await prisma.$disconnect()
process.exit(0)
})
setTimeout(() => { process.exit(1) }, 10000) // Force exit if hung
}
process.on('SIGINT', () => gracefulShutdown('SIGINT'))
process.on('
SIGTERM', () => gracefulShutdown('SIGTERM'))
Close HTTP server first (stops new requests), then $disconnect() database, add 10s timeout to force exit if cleanup hangs. Fastify simplifies this with fastify.addHook('onClose', () => prisma.$disconnect()).
Test Suites (Jest, Vitest, Mocha)
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
afterAll(async () => {
await prisma.$disconnect();
});
beforeEach(async () => {
await prisma.user.deleteMany(); // Clean data, NOT connections
});
test('creates user', async () => {
const user = await prisma.user.create({
data: { email: 'test@example.com', name: 'Test' },
});
expect(user.email).toBe('test@example.com');
});
Use single PrismaClient instance across all tests; $disconnect() only in afterAll(); clean database state between tests, not connections.
For Vitest setup configuration (setupFiles, global hooks), see vitest-4/skills/configuring-vitest-4/SKILL.md.
Serverless Functions (AWS Lambda, Vercel, Cloudflare Workers)
Do NOT disconnect in handlers â breaks warm starts (connection setup every invocation). Use global singleton pattern with connection pooling managed by CLIENT-serverless-config. Exception: RDS Proxy with specific requirements may benefit from explicit $disconnect().
Next.js
Development: No explicit disconnect needed; Next.js manages lifecycle. Production: Depends on deploymentâfollow CLIENT-serverless-config for serverless, server pattern for traditional deployment.
Logging Configuration
| Environment | Config | Output |
|---|---|---|
| Development | log: ['query', 'info', 'warn', 'error'] |
Every SQL query with parameters, connection events, warnings/errors |
| Production | `log: [‘warn |
‘, ‘error’]| Only warnings and errors; reduced log volume, better performance | | **Environment-based** |log: process.env.NODE_ENV === ‘production’ ? [‘warn’, ‘error’] : [‘query’, ‘info’, ‘warn’, ‘error’]` | Conditional verbosity |
Custom event handling:
const prisma = new PrismaClient({
log: [
{ emit: 'event', level: 'query' },
{ emit: 'event', level: 'error' },
{ emit: 'stdout', level: 'warn' },
],
});
prisma.$on('query', (e) => {
console.log(`Query: ${e.query} (${e.duration}ms)`);
});
prisma.$on('error', (e) => {
console.error('Prisma Error:', e);
});
Constraints & Validation
MUST:
- Call $disconnect() in server shutdown handlers (SIGINT, SIGTERM); in test afterAll/global teardown; await completion before process.exit()
- Use environment-based logging (verbose dev, minimal prod)
SHOULD:
- Add 10s timeout to force exit if shutdown hangs
- Close HTTP server before disconnecting database
- Use framework hooks when available (Fastify onClose, NestJS onModuleDestroy)
- Log shutdown progress
NEVER:
- Disconnect in serverless function handlers (breaks warm starts)
- Disconnect between test cases (only in afterAll)
- Forget await on $disconnect()
- Exit process before $disconnect() completes
Validation:
- Manual: Start server, Ctrl+C, verify “Database connections closed” log and clean exit
- Tests: Run
npm testâ expect no “jest/vitest did not exit” warnings, no connection errors - Leak detection: Run tests 10x â no “Too many connections” errors or timing degradation
- Logging dev: NODE_ENV=development, verify query logs appear on DB operations
- Logging prod: NODE_ENV=production, verify only warn/error logs appear, successful queries silent
Common Issues & Solutions
| Issue | Cause | Solution |
|---|---|---|
| “jest/vitest did not exit” warning | Missing $disconnect() in afterAll() | Add afterAll(async () => { await prisma.$disconnect() }) |
| “
Too many connections” in tests | New PrismaClient created per test file | Use global singleton pattern (see Vitest setup above) |
| Process hangs on shutdown | Forgot await on $disconnect() | Always await prisma.$disconnect() |
| Serverless cold starts very slow | Disconnecting in handler breaks warm starts | Remove $disconnect() from handler; use connection pooling |
| Connection pool exhausted after shutdown | $disconnect() called before server.close() | Reverse order: close server first, then disconnect |
Framework-Specific Notes
Express.js: Use server.close() before $disconnect(); handle SIGINT + SIGTERM; add timeout for forced exit.
Fastify: Use onClose hookâframework handles signal listeners and ordering automatically.
NestJS: Implement onModuleDestroy lifecycle hook; use @nestjs/terminus for health checks; automatic cleanup via module system.
Next.js: Dev modeâno explicit disconnect needed. Productionâdepends on deployment (serverless: see CLIENT-serverless-config; traditional: use server pattern). Server Actions/API Routesâfollow serverless pattern.
Serverless (Lambda, Vercel, Cloudflare): Defaultâdo NOT disconnect in handlers. ExceptionâRDS Proxy with specific config. See CLIENT-serverless-config for connection management.
Test Frameworks: JestâafterAll() in files or global teardown. Vitestâglobal setupFiles. Mochaâafter() in root suite. PlaywrightâglobalTeardown for E2E.
Related Skills
- CLIENT-singleton-pattern: Ensuring single PrismaClient instance
- CLIENT-serverless-config: Serverless-specific connection management
- PERFORMANCE-connection-pooling: Optimizing connection pool size
Next.js Integration:
- If implementing data access layers with session verification, use the securing-data-access-layer skill from nextjs-16 for authenticated database patterns