Contents
NestJSとPrismaの統合: 実践的なチュートリアル
NestJSプロジェクトにPrismaを組み込むことで、データベース操作の効率性や保守性が大きく向上します。本記事では、サービス層・リポジトリ層に焦点を当てた実装方法を解説し、「NestJS Prisma 統合 チュートリアル」として具体的な手順を提供します。
導入: NestJSプロジェクトにおけるPrismaの役割
NestJSとPrismaはTypeScriptとの連携に強みを持ち、ORM初心者でも簡単にデータベース操作が可能です。開発効率と保守性の向上を目的として、本記事では以下の範囲をカバーします。
主な実装対象
- Prisma Clientのインストールと設定
- データモデル定義とマイグレーション手順
- サービス層でのPrisma利用のベストプラクティス
- 依存性注入によるテスト対応設計
- 統合テストの実装方法
Prisma Clientのインストールと初期設定
NestJSプロジェクトにPrismaを導入するには、CLIツールとClientライブラリをインストールします。この手順はプロジェクトの基盤となるため、正確な実行が不可欠です。
prisma CLIの導入手順
以下のように操作してください:
-
プロジェクトルートでprisma initコマンドを実行
bash
npx prisma init
このコマンドにより、prisma/schema.prismaファイルと.envが自動生成されます。 -
データベース接続設定の確認
.envファイルには以下のように環境変数を記述します:
env
DATABASE_URL="postgresql://user:password@localhost:5432/mydb?schema=public"
注意: 接続情報はセキュリティ上、本番環境では
.envファイルを使用して管理する必要があります。
エンティティ定義とマイグレーションの実践
Prismaではデータベースモデルをprisma.schemaで定義し、自動生成されたTypeScript型を使って操作します。この設計により、コードとDBスキーマの同期が容易になります。
prisma.schemaでのモデル定義例
以下のようにモデルを記述します:
|
1 2 3 4 5 6 |
model User { id Int @id @default(autoincrement()) name String email String @unique } |
この定義は自動的にTypeScriptインターフェースとして生成され、prisma generateコマンド実行時に反映されます。
マイグレーション手順と管理
データベース構造変更時は以下の流れで処理します:
-
モデルを更新
prisma.schemaに新しいフィールドやリレーションを追加します。 -
マイグレーション生成と適用
bash
npx prisma migrate dev --name add_age_field
これにより、migrations/ディレクトリに差分ファイルが作成され、データベース構造が更新されます。
補足: マイグレーションはデベロッパーアカウントで実行されるため、本番環境では慎重に管理する必要があります。
サービス層でのPrisma Client利用ベストプラクティス
サービス層ではリポジトリパターンを採用し、PrismaClientの依存性注入を活用します。この設計により、コードの再利用性やテスト性が向上します。
リポジトリパターンとは?
- 目的: データアクセスロジックとビジネスロジックを分離
- 利点:
- テスト対応しやすい
- サービス層の単一責任原則が守れる
- 型安全な設計が可能
UserRepositoryクラスの実装例
以下のようにリポジトリを定義します:
|
1 2 3 4 5 6 7 8 9 10 11 |
@Injectable() export class UserRepository { constructor(private readonly prisma: PrismaService) {} async findUserById(id: number): Promise<User | null> { return this.prisma.user.findUnique({ where: { id }, }); } } |
このようにリポジトリを定義することで、サービス層は単一責任を持つようになり、テスト性が向上します。
PrismaServiceの定義と注入方法
PrismaServiceは@Injectable()で提供されるクラスであり、PrismaClientのインスタンスを管理する役割を持ちます。以下に具体的な定義例を示します。
PrismaServiceの実装
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@Injectable() export class PrismaService extends PrismaClient implements OnModuleInit { async onModuleInit() { await this.$connect(); } async enableShutdownHooks(app: INestApplication) { this.$on('beforeExit', async () => { await this.$disconnect(); }); } } |
モジュールでの注入
app.module.tsに以下のように提供します:
|
1 2 3 4 5 6 7 8 9 10 11 |
@Module({ providers: [ PrismaService, { provide: 'USER_REPO', useClass: UserRepository, }, ], }) export class AppModule {} |
トランザクション処理の実装例
複数のデータ操作が必要な場合、$transaction()メソッドでトランザクションを開始します。
|
1 2 3 4 5 6 7 8 9 10 |
async updateUserProfile(id: number, data: Partial<User>) { return this.prisma.$transaction(async (prisma) => { await prisma.user.update({ where: { id }, data, }); // 他の操作をここに追加可能 }); } |
補足: データ一貫性が必要な処理(例:ユーザー登録とメール送信)ではトランザクションを使用します。
テスト対応設計: 依存性注入によるモック化
テストでは、PrismaClientをモックすることで、外部依存を切り離したテストが可能です。以下に具体的な方法を示します。
モックの実装例
test/user.repository.mock.ts:
|
1 2 3 4 5 6 |
import { UserRepository } from './user.repository'; export class MockUserRepository implements Partial<UserRepository> { findUserById = jest.fn().mockResolvedValue({ id: 1, name: 'テスト' }); } |
統合テストの設計と実装
統合テストでは、supertestを用いてエンドポイントとデータベース操作を同時に検証します。以下に実装手順を示します。
E2Eテストでのデータベース接続設定
-
jest.config.tsに環境変数を設定
ts
testEnvironment: 'node',
setupFilesAfterEnv: ['<rootDir>/test/setup-jest.ts'], -
テスト用のマイグレーションを適用
bash
npx prisma migrate reset --force
テスト後のリセット処理
|
1 2 3 4 |
afterEach(async () => { await prisma.$executeRaw`TRUNCATE TABLE "User"`; }); |
これにより、各テスト終了後にデータベースをクリーンに保つことができます。
結論: 記事の要点
| 項目 | 内容 | 補足 |
|---|---|---|
| PrismaService | データベース接続とトランザクション管理を行う | @Injectable()で提供する |
| リポジトリパターン | テスト性・保守性向上に貢献 | サービス層と分離する |
| マイグレーション | 自動生成機能を活用して構造を管理 | 本番環境での実行は慎重に |
NestJSとPrismaの統合は、効率的な開発と保守性向上のためには欠かせない技術です。TypeScriptとの連携で型安全な設計が可能になり、プロダクト品質の向上につながります。