Creating a new database connection per request exhausts the Postgres connection limit in minutes under load. Postgres allows roughly 100 connections by default (adjustable but resource-constrained); a serverless function that opens a new PrismaClient() or new Pool() per invocation can exhaust this from a single spike of concurrent requests, producing too many connections errors for all users. CWE-400 (uncontrolled resource consumption) applies directly: connection handles are a finite resource, and unbounded creation degrades the service for all tenants. ISO 25010 resource-utilisation captures the connection efficiency requirement.
High because new-connection-per-request exhausts the Postgres connection limit rapidly under moderate concurrency, producing hard failures for all users rather than degraded performance.
Use a module-level singleton pool or the Prisma global singleton pattern so one pool instance is shared across all request handlers. For serverless deployments, use a serverless-compatible driver or connection pooler.
// lib/prisma.ts — Next.js singleton (prevents hot-reload exhaustion)
import { PrismaClient } from '@prisma/client'
const g = globalThis as { prisma?: PrismaClient }
export const prisma = g.prisma ?? new PrismaClient()
if (process.env.NODE_ENV !== 'production') g.prisma = prisma
// lib/db.ts — pg Pool singleton for long-running servers
import { Pool } from 'pg'
export const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 20,
idleTimeoutMillis: 30_000,
})
For Neon or Supabase on Vercel Edge, use the serverless HTTP driver (@neondatabase/serverless) instead of a persistent pool — HTTP-per-query is correct for stateless edge runtimes.
ID: database-design-operations.query-patterns.connection-pooling
Severity: high
What to look for: Count every database client or connection initialization point in the codebase. Read the database setup file (src/db/index.ts, src/lib/db.ts, src/lib/prisma.ts, lib/db/client.ts, or similar). Enumerate every backup mechanism configured and check whether: (1) A new Pool() or new Client() is created inside a request handler (wrong — creates a new connection per request). (2) A module-level singleton is used (correct — one pool shared across requests). (3) For Prisma in serverless environments (Vercel, Netlify, Cloudflare), check for the global singleton pattern to prevent connection exhaustion from module re-instantiation in hot-reload or concurrent executions. (4) For serverless databases (Neon, PlanetScale, Supabase), check if the serverless-compatible driver is used (@neondatabase/serverless, Supabase connection pooler on port 6543). Check connection pool configuration: max connections should be set appropriately (not unlimited, not too restrictive).
Pass criteria: Database connection uses a singleton pool — at least 1 pool instance shared across all request handlers (module-level Pool instance, global Prisma client, or managed pooler). Serverless environments use a serverless-compatible driver or connection pooler. Pool max connections is configured.
Fail criteria: new Client() or new Pool() created inside a request handler function. new PrismaClient() instantiated on every request without the global singleton pattern. No pooling for a serverless deployment that makes concurrent requests.
Skip (N/A) when: Application uses a fully managed database client that handles pooling internally and requires no configuration (e.g., Supabase JS client for browser-side queries via the Supabase API layer, Firebase SDK).
Detail on fail: Specify the file and pattern. Example: "In src/app/api/users/route.ts: new Pool() created inside the GET handler — a new connection is opened for every request." or "lib/prisma.ts creates new PrismaClient() without global singleton pattern — connection exhaustion likely in serverless hot-reload.".
Remediation: Use a singleton pool:
// lib/db.ts — singleton pg Pool (long-running server)
import { Pool } from 'pg'
export const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 20, // maximum connections in pool
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
})
// lib/prisma.ts — Prisma singleton for serverless (Next.js)
import { PrismaClient } from '@prisma/client'
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient }
export const prisma =
globalForPrisma.prisma ??
new PrismaClient({
log: process.env.NODE_ENV === 'development' ? ['query', 'warn', 'error'] : ['warn', 'error'],
})
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
// For Neon serverless (Vercel Edge/Cloudflare Workers)
import { neon } from '@neondatabase/serverless'
const sql = neon(process.env.DATABASE_URL!)
// sql is a function — no persistent connection, uses HTTP for each query
// Appropriate for edge runtimes; use pg Pool for Node.js runtimes