Next.js の開発時に Prisma が「too many connections」で接続できない
Next.js の Fast Refresh でモジュールが再評価されるたびに new PrismaClient() が実行され、開発中だけ接続が増え続けるのが原因。
PrismaClient を globalThis にキャッシュして単一インスタンスを使い回せば解消する。
公開: 更新:
node reload_bad.js > reload.log 2>&1 & … reload 8: Too many database connections opened: FATAL: remaining connection slots are reserved for non-replication superuser connections
2026-10-15 に node:20(Prisma 6.19.3 / Node.js 20.20.2) で実際に打って取った出力。検証環境の詳細
要約
Next.js の開発サーバーは Fast Refresh(hot reload)でファイルを保存するたびにモジュールを再評価します。
このとき PrismaClient を生成するモジュールも作り直され、保存ごとに新しいインスタンスと DB 接続が積み上がります。
結果として開発中だけ接続が増え続け、too many connections(PostgreSQL では sorry, too many clients already)に達します。
解決策は単純で、PrismaClient を globalThis にキャッシュし、開発環境では同じインスタンスを使い回すことです。
実行例
reload_bad.js は再読み込みが 7 回目までは通り、8 回目で Too many database connections opened になり、そのとき pg_stat_activity には app ユーザーの接続が 7 本残っていた。PrismaClient を使い回す reload_good.js は同じ再読み込みを 10 回繰り返しても最後まで ok で、接続は 1 本のままだった。
$ node reload_bad.js > reload.log 2>&1 &$ cat reload.log
reload 1: ok
reload 2: ok
reload 3: ok
reload 4: ok
reload 5: ok
reload 6: ok
reload 7: ok
reload 8: Too many database connections opened: FATAL: remaining connection slots are reserved for non-replication superuser connections$ psql postgresql://postgres:pass@localhost:5432/app -c "SELECT count(*) AS app_connections FROM pg_stat_activity WHERE usename = 'app';"
app_connections
-----------------
7
(1 row)$ node reload_good.js > reload.log 2>&1 &$ cat reload.log
reload 1: ok
reload 2: ok
reload 3: ok
reload 4: ok
reload 5: ok
reload 6: ok
reload 7: ok
reload 8: ok
reload 9: ok
reload 10: ok$ psql postgresql://postgres:pass@localhost:5432/app -c "SELECT count(*) AS app_connections FROM pg_stat_activity WHERE usename = 'app';"
app_connections
-----------------
1
(1 row)— 2026-10-15 時点の出力
検証環境Node.js 20.20.2 / npm 10.8.2 / Python 3.11.2 / 2026-10-15 検証(ほか 2 件)
- 検証日
- 実行環境
node:20Debian GNU/Linux 12 (bookworm)- バージョン
- Node.js 20.20.2
- npm 10.8.2
- Python 3.11.2
- Git 2.39.5
- Prisma 6.19.3
この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。
解決策
1. globalThis にインスタンスをキャッシュする
公式の Database connections(新しいタブで開く) が推奨する書き方です。
// lib/prisma.ts
import { PrismaClient } from "../prisma/generated/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;
}グローバル変数は hot reload で初期化されないため、2 回目以降は既存インスタンスを再利用します。
読み込み元は schema.prisma の generator に指定した output のパスです。
Prisma ORM 7 では output が必須になったため、生成先からの相対パスで読み込みます(6 までで output を指定していない構成なら @prisma/client のままで動きます)。
2. アプリ側は常にこのモジュールを読み込む
import { prisma } from "@/lib/prisma";
const users = await prisma.user.findMany();各所で new PrismaClient() せず、必ずこの単一モジュール経由で使います。
3. 滞留した接続を解放する
すでに接続が詰まっている場合は、開発サーバー(next dev)を一度止めて再起動すれば、開いたままの接続が解放されます。
よくある原因
- hot reload でモジュールが再評価され、
new PrismaClient()が繰り返し実行される。 - モジュールスコープで直接インスタンスを生成しており、再読み込みのたびに別接続になる。
- 開発サーバーを長時間起動したままにして、DB の最大接続数に到達する。
本番ビルドでは hot reload が無いため再現せず、開発時だけ起きるのが特徴です。