Symptom
After ~10 Turbopack hot-reloads in npm run dev, all DB-touching pages fail with:
Error: Failed query: select ...
[cause]: PostgresError: sorry, too many clients already
severity: 'FATAL', code: '53300'
Recovery requires restarting the dev server.
Root cause
src/db/index.ts:6 instantiates the Postgres client at module top level with no globalThis caching:
const client = postgres(connectionString);
export const db = drizzle(client, { schema });
Turbopack's HMR re-evaluates this module on every change. Each evaluation opens a new pool (postgres-js default max: 10) and never closes the old one. Postgres's default max_connections is 100, so we hit the cap after ~10 reloads.
Production isn't affected — Next.js doesn't HMR there, the module loads once, the pool lives for the process lifetime.
Proposed fix
Standard pattern: cache the client on globalThis in non-prod so HMR reuses it. Same shape Prisma, Drizzle, and Next.js examples all use.
// src/db/index.ts
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
import * as schema from "./schema";
const connectionString = process.env.DATABASE_URL!;
const globalForPostgres = globalThis as unknown as {
__pgClient?: ReturnType<typeof postgres>;
};
const client = globalForPostgres.__pgClient ?? postgres(connectionString);
if (process.env.NODE_ENV !== "production") {
globalForPostgres.__pgClient = client;
}
export const db = drizzle(client, { schema });
Optional: also cap postgres(..., { max: 10 }) explicitly so any future regression has a smaller blast radius.
Acceptance criteria
Symptom
After ~10 Turbopack hot-reloads in
npm run dev, all DB-touching pages fail with:Recovery requires restarting the dev server.
Root cause
src/db/index.ts:6instantiates the Postgres client at module top level with noglobalThiscaching:Turbopack's HMR re-evaluates this module on every change. Each evaluation opens a new pool (postgres-js default
max: 10) and never closes the old one. Postgres's defaultmax_connectionsis 100, so we hit the cap after ~10 reloads.Production isn't affected — Next.js doesn't HMR there, the module loads once, the pool lives for the process lifetime.
Proposed fix
Standard pattern: cache the client on
globalThisin non-prod so HMR reuses it. Same shape Prisma, Drizzle, and Next.js examples all use.Optional: also cap
postgres(..., { max: 10 })explicitly so any future regression has a smaller blast radius.Acceptance criteria
npm run dev, queries still succeed (no53300).globalThispollution in production builds (gated onNODE_ENV).