Next.js で Prisma ORM を使う包括的なガイド
Prisma ORMとNext.jsは、モダンなサーバーサイドレンダリングおよびAPI駆動型ウェブアプリケーションを構築するための強力な組み合わせです。このガイドでは、それらの可能性を最大限に引き出すためのさまざまなヒントと戦略をまとめています。ベストプラクティス、モノレポのセットアップガイダンス、または動的な利用戦略をお探しの場合でも、すべてカバーしています。
開発におけるPrisma Client使用のベストプラクティス
複数のPrisma Clientインスタンスを避ける
Next.jsアプリケーションを開発する際によくある問題の1つは、Prisma Clientのインスタンスを誤って複数作成してしまうことです。これは、開発中のNext.jsのホットリロード機能が原因で発生することがよくあります。
なぜこれが起こるのか
Next.jsのホットリロード機能は、コードの変更を即座に反映させるためにモジュールを頻繁にリロードします。しかし、これによりPrisma Clientのインスタンスが複数作成され、リソースを消費したり、予期せぬ動作を引き起こす可能性があります。
推奨される解決策
これを避けるためには、グローバル変数を使用してPrisma Clientの単一インスタンスを作成します
// lib/prisma.ts
import { PrismaClient } from "@prisma/client";
const globalForPrisma = global as unknown as { prisma: PrismaClient };
export const prisma =
globalForPrisma.prisma || new PrismaClient();
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
このアプローチを使用することで、開発中のホットリロード中でもPrisma Clientのインスタンスが1つだけ存在することが保証されます。
モノレポでのPrisma ORMのセットアップ
モノレポでPrisma ORMを使用する際の課題
モノレポは複数のプロジェクト間でコードと依存関係を共有できるため、現代の開発で人気のある選択肢です。しかし、モノレポでPrisma ORMを使用する場合、依存関係の解決とスキーマ管理に関連する課題が生じることがあります。
主要な問題
- 依存関係の解決: モノレポ内の複数のパッケージが異なるバージョンのPrisma ORMを使用している場合、競合が発生する可能性があります。
- スキーマの一元化: 複数のプロジェクトにわたる単一のPrismaスキーマを管理するのは複雑になる可能性があります。
モノレポ統合のベストプラクティス
-
Prismaスキーマを一元化する: 一貫性を確保するため、
schema.prisma
ファイルを@myorg/db
のような共有パッケージに配置します。 -
生成されたクライアントにカスタム出力ディレクトリを使用する: パッケージ間の一貫性を保つため、生成されたPrisma Clientのカスタム出力ディレクトリを定義します。
-
依存関係をルートにインストールする: バージョン競合を防ぐため、Prisma ORMをモノレポのルートにインストールします。個々のパッケージがPrismaへの直接アクセスを必要とする場合(例:ローカルクライアント生成のため)、それらのパッケージ内にもインストールします。Turborepoのようなモノレポツールを使用し、そのベストプラクティスに従うか、アプリ全体で依存関係を同期させる同様の戦略を採用してください。
-
生成にNPMスクリプトを使用する:
{
"scripts": {
"prisma:generate": "prisma generate --schema=./packages/db/schema.prisma"
}
}
このアプローチにより、モノレポ内のすべてのプロジェクトでPrismaスキーマと生成されたクライアントが同期されます。
Next.jsにおけるPrisma Clientの動的利用
動的なシナリオの処理
テナント固有のデータベースを使用するような動的なユースケースでは、Next.jsでPrisma ORMを使用する際にさらなる考慮が必要です。
問題
各テナントが独自のデータベースを持つ可能性があり、ランタイムで個別のPrisma Clientを作成する必要が生じます。Next.jsはそのハイブリッドレンダリングモデルのため、これは複雑になる可能性があります。
解決策
ファクトリ関数を使用して、テナント固有の設定に基づいてPrisma Clientを動的に作成します
// lib/prismaDynamic.ts
import { PrismaClient } from "@prisma/client";
type TenantConfig = {
databaseUrl: string;
};
export function createPrismaClient(config: TenantConfig): PrismaClient {
return new PrismaClient({
datasources: {
db: {
url: config.databaseUrl,
},
},
});
}
リソース枯渇を避けるため、動的に作成されたPrisma Clientのライフサイクルを管理するようにしてください。