はじめに
本記事では、NestJSとPrismaを用いて記事投稿APIを実装する方法を、基本から一歩ずつ丁寧に解説していきます。
NestJSは、高い拡張性と保守性を誇るフレームワークとして注目を集めており、中〜大規模アプリケーションにおいても安心して採用できる技術です。そしてPrismaは、型安全かつ直感的な操作が可能なORMとして、TypeScriptとの相性が非常に良く、近年多くのプロジェクトで採用されています。
想定読者
本記事は、NestJSの基本的な構造をある程度理解している方を対象としています。とくに、
- NestJSの基本的なプロジェクト構成は知っているが、実用的なAPIの組み方に慣れていない方
- Prismaを使ってみたいが、実際のAPI開発にどう落とし込むかを体感したい方
- 単なる動作確認ではなく、「現場で役立つ」APIの構成を学びたい方
今回のゴール
本記事では、次のような成果物を目指します。
- Prismaを導入し、記事(Post)モデルを定義・マイグレーションできるようになる
- NestJS上に記事投稿に関するCRUD API(一覧取得、投稿、詳細取得、更新、削除)を構築する
- バリデーションやDTO設計を取り入れ、堅牢な構成で実装できる
この構成を押さえておくことで、実際のプロダクト開発にも耐えられる「現場対応力」が上がるはずです。
PostmanによるAPIテストで実際に記事の投稿や参照ができるところまで実装します。
この連載の全体像と今回の位置づけ
連載構成
- NestJSで始めるWebアプリ開発 ─ ブログサイトを作りながら学ぶプロジェクト構成と設定管理
- NestJSで記事投稿APIを作ろう ─ Prisma導入とCRUD実装の基本 ←
今回の記事
- アプリの信頼性を高める ─ ロギング・エラーハンドリング・テスト戦略
- PrismaとDB設計を深掘る ─ モデル・リレーション・運用設計
- ReactでUIを構築して本番へデプロイ ─ Dockerと環境構築
DockerでPostgreSQLを立てる
まずは、NestJS + Prisma が接続できるように、Docker上にPostgreSQLを立てます。
docker-compose.yml
をプロジェクト直下に作成
services:
db:
image: postgres:15
container_name: nestjs-postgres
ports:
- "5432:5432"
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: nestjs_blog
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:
- ボリュームで永続化しておけば、コンテナの再起動でもデータが消えません
起動コマンド
バックグラウンドでコンテナが立ち上がります。
docker-compose up -d
DB接続確認
docker exec -it nestjs-postgres psql -U postgres -d nestjs_blog
コンテナに入れたらデータベース一覧を確認します。
\l
nestjs_blog
が表示されればOKです。
コンテナに直接入って作業を行うことは特にないので、いったん抜けます。
\q
.env
に接続情報を記述
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/nestjs_blog"
この接続文字列で、Prisma ClientがDocker上のPostgreSQLにアクセス可能になります。
Prismaの導入と初期設定
記事投稿APIの基盤となるORM(Object-Relational Mapping)として、今回は Prisma を採用します。Prismaは、TypeScriptと強く結びついたモダンなORMで、型安全かつ直感的にDB操作ができるのが大きな特徴です。
まずは、NestJSプロジェクトにPrismaを導入し、データベースとの接続設定を整えていきましょう。
Prismaのインストール
まずは、必要なパッケージをインストールします。NestJSプロジェクトのルートで、以下のコマンドを実行してください。
cd backend
npm install prisma --save-dev
npm install @prisma/client
prisma
はCLIやマイグレーション用のツール@prisma/client
は、アプリケーションからDB操作を行うためのライブラリです
prisma init の実行
次に、Prismaの初期化コマンドを実行します。
npx prisma init
prisma/schema.prisma
:モデル定義ファイルが作成されます。
generator client {
provider = "prisma-client-js"
output = "../generated/prisma"
}
このように設定されていると、後ほど実施するPrisma Clientの生成で出力先が/generate
となりNestJSで正しく認識できなくなるため削除します。
generator client {
provider = "prisma-client-js"
- output = "../generated/prisma"
}
モデル定義:記事(Post)モデルの作成
Prismaの導入と接続設定が完了したので、次は記事投稿用のモデルを定義します。
ここでは、Post という名前のモデルを作成し、記事投稿に必要なフィールドを設計します。記事にはタイトル、本文、作成日時、更新日時などが含まれる想定です。
schema.prismaの編集
backend/prisma/schema.prisma を以下のように編集します。
model Post {
id Int @id @default(autoincrement())
title String
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
この定義により、Post
モデルには以下のフィールドが含まれます
フィールド名 | 型 | 補足 |
---|---|---|
id | Int | 主キー。自動で連番が振られます |
title | String | 記事のタイトル |
content | String | 記事の本文 |
createdAt | DateTime | 作成日時。自動的に現在時刻を設定 |
updatedAt | DateTime | 更新日時。レコード更新時に自動更新 |
Prismaでは @default
や @updatedAt
といったアノテーションで、フィールドの初期値や自動更新の挙動を定義できます。
マイグレーションの実行
モデルを定義したら、データベースにテーブルを作成するためのマイグレーションを実行します。
cd backend
npx prisma migrate dev --name init
このコマンドを実行すると、以下のような処理が行われます。
migrations/
ディレクトリにマイグレーション履歴が生成されます- Prismaのモデル定義に基づいて、PostgreSQL上に
Post
テーブルが作成されます @prisma/client
が自動的に再生成され、型安全なDBアクセスが可能になります
テーブル作成の確認(Prisma Studio を使用)
マイグレーションが完了したので、Prisma Studioを使ってテーブルの作成状況を確認します。
Prisma Studioは、Prismaが提供しているWebベースのデータブラウザで、ローカルDBの中身をGUIで確認できる便利なツールです。
以下のコマンドで起動します。
npx prisma studio
コマンドを実行すると、ブラウザが起動し、Post
テーブルが表示されていることを確認できます。
テーブル名が左サイドバーに表示され、クリックすることで中身の確認やレコードの追加・編集も可能です。
GUIで確認できるので、マイグレーションが正しく適用されたかを直感的に把握できます。
CLIでの確認ももちろん可能ですが、作業効率と視認性を優先して、今後はPrisma Studioでの確認を基本とすることにします。
次は、この Post モデルを使って、実際にデータを読み書きするためのAPI(CRUD処理)を実装していきます。モデルとテーブルが整ったことで、バックエンドとしての骨格が見えてきました。
Prisma Clientの生成とNestJSへの統合
記事モデルを定義し、マイグレーションによってデータベースにテーブルを作成したところまでで、データ構造は整いました。
次に行うのは、そのデータベースにアクセスするための仕組みを用意することです。
Prisma Clientとは?
Prismaでは、schema.prisma に書いたモデル定義から、Prisma Client というクラスが自動生成されます。これは、データベースにアクセスするためのコードです。
prisma.post.findMany() // Postテーブルの全件取得
prisma.post.create({ data: { title, content } }) // 新規レコード作成
といった形で、型安全にDB操作が行えるクライアントライブラリになります。
このPrisma Clientは @prisma/client
パッケージの中に生成され、NestJSからも利用できるようになります。
Prisma Clientの生成
以下のコマンドで生成できます。
cd backend
npx prisma generate
生成されるファイルは node_modules/@prisma/client
に含まれ、Prismaの型情報もここに定義されます。
NestJSでPrismaを使うには?
NestJSでは、直接 new PrismaClient() を呼ぶよりも、NestJSの「依存性注入(DI)」の仕組みを使って各所で共有するのが一般的です。
そのために、Prisma ClientをラップしたPrismaService を作成します。
PrismaServiceとは?
PrismaServiceは、以下のような役割を担うクラスです。
- PrismaClientを継承し、アプリ全体で使い回せるようにする
- NestJSの「サービス」として登録し、Controllerや他のServiceで使えるようにする
- アプリ起動時にDBへ接続し、終了時には切断する
import { PrismaClient } from '@prisma/client';
import { Injectable } from '@nestjs/common';
import { OnModuleDestroy, OnModuleInit } from '@nestjs/common/interfaces/hooks';
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
async onModuleInit() {
await this.$connect(); // アプリ起動時にDB接続
}
async onModuleDestroy() {
await this.$disconnect(); // アプリ終了時に切断
}
}
これによって、アプリケーション全体で一つの PrismaService を使いまわせるようになります。
PrismaService を提供するためのモジュールを作成し、それを必要な場所で import
することで、使いたい場所でPrismaの機能を使えるようにするというのが、NestJSの設計思想です。
import { Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';
@Module({
providers: [PrismaService],
exports: [PrismaService], // 他のモジュールから使えるようにする
})
export class PrismaModule {}
これをこのあと作成する PostModule や AppModule に imports: [PrismaModule] で読み込むことで、NestJSの「依存性注入」で PrismaService がどこでも使えるようになります。
ここまでのまとめ
概念 | 役割 |
---|---|
Prisma Client | Prismaが生成する、型付きのDB操作ライブラリ |
PrismaService | NestJS内でPrisma Clientを共通管理するサービス(接続・切断・注入管理) |
PrismaModule | PrismaServiceを提供・共有するNestJSモジュール |
CRUD APIを作成(Controller / Service の実装)
Post
モデルの定義とテーブルの作成が完了したので、続いては記事投稿に関するAPIのCRUD機能を実装していきます。
ここでは、NestJSのControllerとServiceを用いて、以下の5つのエンドポイントを構築します。
- 記事一覧の取得(GET
/posts
) - 新規記事の作成(POST
/posts
) - 記事の詳細取得(GET
/posts/:id
) - 記事の更新(PATCH
/posts/:id
) - 記事の削除(DELETE
/posts/:id
)
Prisma Clientを使用して、これらの操作をすべて型安全に実行できるようにします。
PostModuleの作成
まずは記事用のモジュール、コントローラ、サービスを一括で作成します。
cd backend
npx nest g resource posts
これにより、src/posts/
配下に必要なファイルが自動生成されました。
posts.controller.ts
posts.service.ts
posts.module.ts
dto/create-post.dto.ts
dto/update-post.dto.ts
この段階で、簡易的なCRUD APIの雛形が整っています。
DTOの型とモデルを一致させる
DTOが PostCreateInput に対応している必要がありますので以下のように修正します。
export class CreatePostDto {
+ title: string;
+ content: string;
}
DTOに関する詳しい解説は後ほどご紹介します。
PrismaServiceの注入
PostService では、データベース操作に PrismaService
を利用します。
NestJSではサービスの依存性を注入するため、PostService
のコンストラクタで以下のように書きます。
import { Injectable } from '@nestjs/common';
import { CreatePostDto } from './dto/create-post.dto';
import { UpdatePostDto } from './dto/update-post.dto';
import { PrismaService } from 'src/prisma/prisma.service';
@Injectable()
export class PostsService {
constructor(private readonly prisma: PrismaService) {}
create(createPostDto: CreatePostDto) {
return this.prisma.post.create({ data: createPostDto });
}
findAll() {
return this.prisma.post.findMany();
}
findOne(id: number) {
return this.prisma.post.findUnique({ where: { id } });
}
update(id: number, updatePostDto: UpdatePostDto) {
return this.prisma.post.update({
where: { id },
data: updatePostDto,
});
}
remove(id: number) {
return this.prisma.post.delete({ where: { id } });
}
}
※差分が多いため最終的なコードを載せておきます。
NestJSでは、サービスを注入するには、モジュールでそのサービスを「提供(provide)」する必要があります。
今回は、PrismaService
を PrismaModule
で提供・エクスポートする構成を取りました。
import { Module } from '@nestjs/common';
import { PostService } from './post.service';
import { PostController } from './post.controller';
+ import { PrismaModule } from 'src/prisma/prisma.module';
@Module({
+ imports: [PrismaModule],
controllers: [PostController],
PostmanによるAPIテスト
コードの修正が終わったら、NestJSを起動します。
cd backend
npm run start:dev
今回はPostManを使ってAPIにアクセスし投稿の登録や参照ができるか確認します。
PostmanのVSCode拡張があるので私はこちらを使って動作確認を行います。
① 新規記事を作成(POST /posts)
- Method: POST
- URL: http://localhost:3000/posts
- Headers:
- Content-Type: application/json
- Body (raw / JSON):
{
"title": "はじめての記事",
"content": "これはNestJS + Prismaで作ったAPIの最初の投稿です。"
}
リクエストが成功すれば、ステータス 201 Created
と作成された記事のJSONが返ってきます。
② 記事一覧を取得(GET /posts)
- Method: GET
- URL: http://localhost:3000/posts
作成した記事が一覧で返ってきます(配列で)。
③ 記事詳細を取得(GET /posts/:id)
- Method: GET
- URL: http://localhost:3000/posts/1
IDに応じて、特定の記事を1件だけ取得できます。
④ 記事を更新(PATCH /posts/:id)
- Method: PATCH
- URL: http://localhost:3000/posts/1
- Headers:
- Content-Type: application/json
- Body (raw / JSON):
{
"title": "記事タイトルを更新しました"
}
部分更新も可能です。レスポンスに更新後のデータが返ります。
⑤ 記事を削除(DELETE /posts/:id)
- Method: DELETE
- URL: http://localhost:3000/posts/1
成功すればステータス 200 OK
と削除された記事の情報が返ります。
その後、再度記事一覧を取得を実行すると1件も返ってこないことを確認できます。
バリデーションとDTOの設計
記事投稿APIに対して、正しいデータだけが送られてくるようにするために、DTOとバリデーションを設計します。
NestJSでは、DTOに class-validator を使ったデコレーターを付けることで、バリデーションロジックを簡潔に実装できます。
DTOとは何か?
DTO(Data Transfer Object)は、リクエストやレスポンスの構造を明示するための型定義です。
NestJSでは、DTOをクラスで定義することで、TypeScriptの型チェックに加え、バリデーションの対象としても機能します。
たとえば、記事の作成時に必要なデータ構造は以下のように設計します。
パッケージのインポート
npm install class-validator class-transformer
CreatePostDtoのバリデーション追加
CLIで自動生成された create-post.dto.ts を次のように編集します。
+ import { IsString, IsNotEmpty } from 'class-validator';
export class CreatePostDto {
+ @IsString()
+ @IsNotEmpty()
title: string;
+ @IsString()
+ @IsNotEmpty()
content: string;
}
これにより、APIに送信されるJSONが以下のような形式であり、かつ空でないことが求められます。
{
"title": "NestJSとPrismaで記事投稿API",
"content": "バリデーションを通すことで、信頼できるデータ構造を担保できます。"
}
UpdatePostDto
部分更新に対応するため、UpdatePostDto ではプロパティを任意とします。
こちらはCLIで自動生成されたコードのままで問題ありません。
import { PartialType } from '@nestjs/mapped-types';
import { CreatePostDto } from './create-post.dto';
export class UpdatePostDto extends PartialType(CreatePostDto) {}
バリデーションを有効にする設定
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
+ import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
+ app.useGlobalPipes(new ValidationPipe());
await app.listen(process.env.PORT ?? 3000);
}
bootstrap().catch((err) => {
console.error('Application failed to start', err);
});
動作確認
NestJSが自動でバリデーションエラーを検出し、エラーレスポンスを返すことを確認します。
新規記事を作成(POST /posts)
- Method: POST
- URL: http://localhost:3000/posts
- Headers:
- Content-Type: application/json
- Body (raw / JSON):
{}
空のデータをPOST /posts
に送った場合は、次のようなレスポンスが返されます。
おわりに
この記事では、NestJSとPrismaを使って、シンプルな記事投稿APIをゼロから構築しました。
Prismaを導入し、PostgreSQLとの接続を確認したうえで、モデルの定義・マイグレーション・Prisma Clientの統合・REST APIの実装・DTOによるバリデーション設計まで、一連の開発フローを段階的に進めてきました。
ここまでの構成によって、
- 型安全で保守性の高いデータ操作
- RESTの設計思想に沿ったエンドポイント構成
- 不正なリクエストを防ぐバリデーションの仕組み
といった、堅牢で実践的なバックエンドAPIの土台が完成しています。
この基礎が整っていれば、今後は以下のような発展的な要素も無理なく組み込めるはずです:
- ログインユーザーと記事を紐づける認証・認可の実装
- ユーザー、コメント、タグなど他エンティティとのリレーション設計
- Prismaのミドルウェアやトランザクションによる高度なDB操作
- SwaggerによるAPIドキュメント自動生成
NestJSとPrismaをベースにしたバックエンド開発は、型安全・柔軟性・可読性のバランスが非常に良く、チーム開発にも向いています。
本記事が、自分の開発スタイルをアップデートするきっかけになれば幸いです。
次回は
ここまでで、記事投稿APIとしての基本機能はひと通り整いました。次回は、これをさらに“実用的なアプリケーション”へと高めていくための工夫に踏み込んでいきます。
- ログの記録と整形:NestJSのLoggerを活用して、重要なイベントを把握しやすく
- エラーハンドリングの統一:独自のExceptionFilterで例外処理の共通化
- テスト戦略の導入:Service層のユニットテスト、Controllerを通したE2Eテストの書き方
- 環境ごとの設定管理:テスト用 .env.test の活用と構成の分離
本番運用を見据えた「信頼されるAPIづくり」の第一歩として、アプリケーションの信頼性を高める実践的なパートとなります。
関連する技術ブログ
NestJSアプリの信頼性を高める ─ ログ・エラーハンドリング・テスト戦略
NestJSアプリを本番品質へと近づけるために、ロギング・例外処理の共通化・ユニットテストとE2Eテストの構築までを丁寧に解説。実践的なコードと構成例を通じて、信頼されるAPI設計の基礎を整えます。
shinagawa-web.com
NestJS × Prismaで深めるDB設計 ─ モデル・リレーション・運用設計
NestJSとPrismaを組み合わせ、実践的なデータベース設計・リレーション定義・マイグレーション運用の基本を整理します。単なるAPI開発に留まらず、運用を見据えた設計力を着実に積み上げ、次回のフロントエンド連携へとつなげます。
shinagawa-web.com
NestJS × React × Railway:ブログUIを実装して本番環境へデプロイ
NestJS+React+Prismaで構成したブログアプリを、Dockerで本番ビルドし、クラウドサービス「Railway」へデプロイするまでの手順をまとめました。本番DB接続、環境変数管理、フロントエンドとバックエンドの統合配信など、運用を意識した実践的な構成に仕上げていきます。個人開発やPoCにも応用できる「即戦力の本番構成」を一緒に組み立てていきましょう。
shinagawa-web.com
NestJSとReactで始めるWebアプリ開発 ─ ブログサイトを作りながら学ぶプロジェクト構成と設定管理
NestJSでブログアプリを開発しながら、Webバックエンドの基礎と実践を体系的に学ぶ連載の第一歩。プロジェクトの初期構成、環境変数管理、将来の拡張を見据えたモノレポ準備まで、堅実な土台作りを一緒に進めていきます。
shinagawa-web.com
チャットアプリ(画像・PDF送信、ビデオ通話機能付き)
お客様固有の要件を除き一般的なチャットアプリに求められる最低限の機能を実装しデモアプリとしてご紹介いたします。
shinagawa-web.com
管理ダッシュボード機能(グラフ表示、データ取り込み)
一般的な家計簿アプリとして求められる最低限の機能を実装しデモアプリとしてご紹介いたします。
shinagawa-web.com
Next.jsとAuth.jsで認証機能を実装するチュートリアル
Next.jsでアプリケーションを作る時に必要となる認証機能をどのように実装するかをご紹介する記事となります。アカウント登録から始まり、ログイン、ログアウト、ページごとのアクセス制御、OAuth、二要素認証、パスワードリセットなど認証に関連する様々な機能をコードベースでご紹介します。
shinagawa-web.com
Next.jsでのメール認証処理の実装ガイド:アカウント登録からトークン検証まで
Next.jsを活用したメール認証の実装方法を解説。アカウント登録時のトークン発行から、Sendgridを使ったメール送信処理まで、具体的な手順を紹介します。
shinagawa-web.com
弊社の技術支援サービス
無駄なコストを削減し、投資対効果を最大化する
クラウド費用の高騰、不要なSaaSの乱立、開発工数の増加――これらの課題に悩んでいませんか?本サービスでは、クラウドコストの最適化、開発効率向上、技術選定の最適化 を通じて、単なるコスト削減ではなく、ROIを最大化する最適解 をご提案します。
shinagawa-web.com
最新技術の導入・検証を支援するPoCサービス
Remix、React Server Components、TypeScript移行、クラウドサービス比較、マイクロサービス、サーバーレス、デザインシステムなど、最新技術のPoC(概念実証)を通じて、最適な技術選定と導入を支援します。貴社の開発課題に合わせた検証・実装で、ビジネスの成長を加速させます。
shinagawa-web.com
開発生産性を最大化するための技術支援
開発チームの生産性向上、コードの品質管理、インフラの最適化まで、様々な側面からサポートします。コードベースのリファクタリングから、テスト自動化、オンボーディング強化まで、プロジェクトの成功に必要なすべての支援を提供。御社の開発現場が効率的に機能するように、技術的な障害を取り除き、スムーズな開発を実現します。
shinagawa-web.com
開発品質向上支援 – 効率的で安定したプロダクトを実現
フロントエンドからバックエンド、データベースまで、開発プロセス全体を最適化し、安定したプロダクト作りをサポートします。コードレビューの仕組み、型定義の強化、E2Eテスト環境の構築など、開発の各ステップにおけるベストプラクティスを導入することで、より効率的でバグの少ない、そしてユーザー満足度の高いサービス提供を支援します。
shinagawa-web.com
Webアプリのセキュリティ強化支援
Webアプリの脆弱性対策からインフラのセキュリティ強化まで、包括的なセキュリティ支援を提供。OWASP Top 10対策、JWT認証の最適化、APIのアクセス制御、依存パッケージの監査、セキュアコーディングの標準化など、実践的なアプローチで開発現場の安全性を向上させます。
shinagawa-web.com
目次
お問い合わせ