はじめに
PrismaをVercelにデプロイしたとき、ローカルでは動くのに本番で以下のようなエラーが出てハマった経験はないでしょうか。
PrismaClient is not a constructor
PrismaClientInitializationError: Unable to require('.../.prisma/client/index.js')
@prisma/client did not initialize yet. Please run "prisma generate" and try to import it again.
これらはPrisma + Vercelの組み合わせで非常によく起きる問題ですが、日本語の情報が少なく解決まで時間がかかりがちです。この記事では原因を5つに絞って解説します。
原因1: prisma generate がビルド時に実行されていない(最多)
Prismaは @prisma/client をインストールするだけでは動きません。prisma generate を実行することで、node_modules/.prisma/client/ にスキーマに対応したクライアントコードが生成されます。
Vercelのビルドはクリーンな環境で実行されるため、ローカルで生成したクライアントファイルは存在しません。postinstall や build スクリプトに追加していないと、本番ビルドで毎回この問題が発生します。
解決策: postinstall スクリプトに追加する
{
"scripts": {
"postinstall": "prisma generate"
}
}
postinstall は npm install 後に自動で実行されるため、Vercelのビルド時にも prisma generate が走るようになります。
build スクリプトに直接含める方法でも対応できます。
{
"scripts": {
"build": "prisma generate && next build"
}
}
**これだけで多くのケースが解決します。**まず最初にこれを試してください。
原因2: Edge Runtime で PrismaClient が使えない
Next.js App Router の Route Handler や Middleware で runtime = 'edge' を指定している場合、@prisma/client はそのままでは動きません。
// ❌ Edge Runtime では動かない
export const runtime = 'edge'
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient() // ここでクラッシュ
@prisma/client はNode.jsのネイティブモジュール(fs・path・net 等)に依存しているため、V8ベースのEdge Runtimeでは使用できません。エラーメッセージが PrismaClient is not a constructor や Module not found になることもあり、原因が分かりにくいです。
解決策1: Edge Runtimeをやめる(最も簡単)
// ✅ runtime 指定を削除するか nodejs に変更
export const runtime = 'nodejs'
DBアクセスが必要なRoute HandlerにEdge Runtimeを使う理由はほぼないため、削除するのが最も安全です。
解決策2: Prisma Accelerate を使う(Edge対応)
Edge Runtimeでも使いたい場合はPrisma Accelerateを導入します。
npm install @prisma/extension-accelerate
import { PrismaClient } from '@prisma/client'
import { withAccelerate } from '@prisma/extension-accelerate'
const prisma = new PrismaClient().$extends(withAccelerate())
Prisma Accelerateはコネクションプーリングも提供するため、Serverless環境全般で有効です。
原因3: Serverless環境でのコネクション枯渇
VercelはServerless Functionをリクエストごとに新しいプロセスで起動します。関数の中で毎回 new PrismaClient() を呼ぶと、リクエストのたびにDBコネクションが増殖して枯渇します。
// ❌ 毎回 new PrismaClient() を生成するとコネクションが枯渇
export async function GET() {
const prisma = new PrismaClient()
const users = await prisma.user.findMany()
await prisma.$disconnect()
return Response.json(users)
}
エラーは Can't reach database server や接続タイムアウトとして現れることが多く、一見すると原因が「コネクション枯渇」とは気づきにくいです。
解決策: グローバルシングルトンパターン
// lib/prisma.ts
import { PrismaClient } from '@prisma/client'
const globalForPrisma = global as unknown as { prisma: PrismaClient }
export const prisma =
globalForPrisma.prisma ||
new PrismaClient({
log: "production" === 'development' ? ['query'] : [],
})
if ("production" !== 'production') globalForPrisma.prisma = prisma
global オブジェクトにインスタンスを保持することで、ホットリロード時に複数インスタンスが生成されるのを防ぎます。
また、接続URLに connection_limit を追加してコネクション数を明示的に制限するのも有効です。
DATABASE_URL="postgresql://user:pass@host/db?connection_limit=5&pool_timeout=10"
PgBouncerなどのコネクションプーラーを使う場合は ?pgbouncer=true も忘れずに。
原因4: 環境変数 DATABASE_URL が設定されていない・反映されていない
Vercelのダッシュボードで環境変数を設定したのに Can't reach database server at '' のようにホスト名が空になるケースがあります。よくある見落としを確認してください。
チェックリスト
- Vercel ダッシュボード → Settings → Environment Variables に
DATABASE_URLが存在するか - スコープ(Production / Preview / Development)が適切に設定されているか
- 設定後にリデプロイを実行したか(環境変数の変更はデプロイ時に読み込まれる)
- 接続文字列に特殊文字(
@・#・%・!等)が含まれていてURLエンコードが必要でないか
特にパスワードに特殊文字が含まれる場合はURLエンコードが必要です。
# パスワードに @ が含まれる場合は %40 にエンコード
DATABASE_URL="postgresql://user:p%40ssword@host:5432/db"
# ^^^
# @ → %40
SupabaseやNeon、PlanetScaleなどのクラウドDBを使っている場合は、そのダッシュボードで「接続文字列をコピー」して貼り付けるのが最も確実です。
原因5: モノレポ環境での output ディレクトリのズレ
Turborepo や Nx などのモノレポ構成では、schema.prisma が packages/database/ のような場所にあり、アプリのルートと異なります。このとき Vercel のビルドから見ると生成されたクライアントの場所が node_modules/.prisma/client/ ではなく、別のパスになります。
monorepo/
├── apps/
│ └── web/ ← Next.js アプリ
└── packages/
└── database/
├── schema.prisma
└── node_modules/.prisma/client/ ← ここに生成される
apps/web/ からは ../../packages/database/node_modules/.prisma/client/ を参照する必要があり、デフォルトの @prisma/client のインポートでは見つからないことがあります。
解決策: output を明示指定する
// packages/database/schema.prisma
generator client {
provider = "prisma-client-js"
output = "./generated/client"
}
// packages/database/index.ts
// ✅ output で指定したパスから直接 re-export
export { PrismaClient } from './generated/client'
// apps/web/lib/prisma.ts
// ✅ パッケージ経由でインポート
import { PrismaClient } from '@your-org/database'
まとめ
Prisma + Vercel で発生するエラーの原因と対策を整理します。
| エラー / 症状 | 原因 | 対策 |
|---|---|---|
PrismaClient is not a constructor |
prisma generate 未実行 |
postinstall に追加 |
| Edge Runtime でクラッシュ | Edge非対応 | runtime = 'nodejs' に変更 |
| コネクションタイムアウト | 毎回 new PrismaClient() |
グローバルシングルトン化 |
Can't reach database server at '' |
環境変数未設定 / 未反映 | Vercelダッシュボードで確認・再デプロイ |
| モノレポでインポートエラー | output パスのズレ | output を明示指定 |
まず postinstall: "prisma generate" を追加するだけで大半のケースは解決します。それでも解決しない場合は、Edge Runtimeの使用有無・コネクション管理・環境変数の順に確認してみてください。Serverlessとデータベースの相性問題は最初は分かりにくいですが、一度仕組みを理解すると同じ問題で詰まることはなくなります。
