はじめに
NestJS は、TypeScript で API サーバーを構築するためのフレームワークです。Module / Controller / Service の明確なレイヤー構造により、スケーラブルなアプリケーションを設計できます。
本記事では、NestJS と Prisma を組み合わせた実践的な API 設計パターンを紹介します。
プロジェクト構成
src/
├── app.module.ts
├── prisma/
│ ├── prisma.module.ts
│ └── prisma.service.ts
├── users/
│ ├── users.module.ts
│ ├── users.controller.ts
│ ├── users.service.ts
│ └── dto/
│ ├── create-user.dto.ts
│ └── update-user.dto.ts
└── auth/
├── auth.module.ts
├── auth.controller.ts
└── auth.service.ts
Prisma Service の実装
import { Injectable, OnModuleInit, OnModuleDestroy } from "@nestjs/common";
import { PrismaClient } from "@prisma/client";
@Injectable()
export class PrismaService
extends PrismaClient
implements OnModuleInit, OnModuleDestroy
{
async onModuleInit() {
await this.$connect();
}
async onModuleDestroy() {
await this.$disconnect();
}
}
Controller の設計
@Controller("users")
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Post()
create(@Body() dto: CreateUserDto) {
return this.usersService.create(dto);
}
@Get()
findAll(@Query() query: PaginationDto) {
return this.usersService.findAll(query);
}
@Get(":id")
findOne(@Param("id") id: string) {
return this.usersService.findOne(id);
}
}
Service の設計
@Injectable()
export class UsersService {
constructor(private readonly prisma: PrismaService) {}
async create(dto: CreateUserDto) {
return this.prisma.user.create({ data: dto });
}
async findAll(query: PaginationDto) {
const { page = 1, limit = 10 } = query;
const [data, total] = await Promise.all([
this.prisma.user.findMany({
skip: (page - 1) * limit,
take: limit,
}),
this.prisma.user.count(),
]);
return { data, total, page, limit };
}
}
設計のポイント
1. Module の責務分離
各 Module は単一の責務を持ちます。依存する Module は imports で明示的に宣言します。
2. DTO によるバリデーション
class-validator と class-transformer を使い、入力値のバリデーションを DTO 層で行います。
3. エラーハンドリング
NestJS の Exception Filter を活用し、統一的なエラーレスポンスを返します。
まとめ
NestJS + Prisma の組み合わせは、TypeScript で型安全な API サーバーを構築するための強力な選択肢です。レイヤー構造を守ることで、チーム開発でも一貫性のあるコードベースを維持できます。