awesome-hacks
Docs

NestJSの基礎

NestJSを何も知らない状態から、全体像と最小構成を掴む

最終更新:2026/06/16

このシリーズで作るもの

このシリーズでは、nestjs-practice-api という小さなユーザー管理APIを作りながら、NestJS案件で 会話についていける / コードレビューできる / 軽微修正できる / API追加できる 状態を目指す。

このシリーズのロードマップ

  1. NestJSの基礎
    Controller / Service / Module / DI の最小構成を掴む。
  2. NestJS DTO / ValidationPipe 入門
    API入口の入力ルールをDTOで定義し、ValidationPipeで検証する。
  3. NestJSでCRUD APIを作る(Prisma + SQLite)
    PrismaとSQLiteを使い、ユーザー管理のCRUDを実装する。
  4. NestJS TypeORM QueryRunnerでトランザクション制御
    TypeORMのQueryRunnerで複数テーブルを跨ぐ処理を明示的なトランザクションで安全にまとめる。
  5. NestJSの例外処理
    APIエラーの返し方を揃える。
  6. NestJS Guard / JWT認証
    ログイン、JWT発行、認証必須APIの基本を押さえる。
  7. NestJS + Swagger
    API仕様をSwagger UIで確認できるようにする。
  8. NestJSをDocker化する(任意)
    ローカル実行環境をコンテナで再現しやすくする。
  9. NestJS BullMQでジョブキューを実装する
    Consumerとジョブキューの基本パターンを実装する。
  10. NestJS Winstonでロギングを整える
    構造化ログ(JSON)と開発用カラーログを環境ごとに切り替える。
  11. NestJSのユニットテスト
    Service・Controller・Guard を TestingModule で単体検証する実践パターンを学ぶ。
  12. NestJS E2Eテスト(Jest + Supertest)
    Supertest でHTTPリクエストを実際に送り、認証フローを含むE2Eテストを実装する。
  13. NestJS PDF非同期生成とトークンダウンロード
    BullMQでPDF生成をキューイングし、PlaywrightでHTMLをPDF変換、使い捨てトークンで安全にダウンロードさせる。

NestJSとは

TypeScript前提として設計された、Node.js保守しやすいサーバーアプリ を作るためのフレームワーク

  • Express/Fastify上で動く
    HTTPサーバー自体はExpressまたはFastifyが実行し、NestJSはその上に「アプリケーション構造」を提供する。 NestJSは内部HTTPエンジンとしてExpressまたはFastifyを選択可能で、デフォルトではExpressを利用する。 Controller / Service / Module / DI などによる構造化された設計を提供しつつ、選択したHTTP基盤(Express/Fastify)のエコシステムも活用できる。

  • Angularの設計思想(バックエンド版Angularという人もいる)

    • Module中心:Module、ControllerServiceが最小単位でありそれぞれに役割分担が明確に決まっており、ModuleでController、Service、Provider、Repositoryを束ねる。
    • Dependency Injection(DI)が標準:NestJS側が自動でインスタンス注入(Expressだと自分でnewする必要がある)。依存関係をコンテナが管理して、テストや差し替えがやりやすい。
    • デコレータ文化:Angular系はデコレータ文化があり、NestJSもかなりそう。@Controller()@Get() のように、クラス/メソッドに役割を付与する。
  • “レイヤー分離”を強く促す
    NestJSは:

    • Module → 配線
    • Controller → HTTP入口
    • Service → 業務ロジック
    • Repository → DBアクセス(Serviceに書くと業務ロジックとSQL/ORMが混ざるため)

    のように責務分離をかなり強く推奨

  • クラスベース設計
    関数文化のReact/Next.jsとは違い、クラス重視

  • “エンタープライズ感”が強い

    • 型安全
    • DI
    • 層分離
    • テスト容易性
    • 保守性
    • 大人数開発

    を重視。
    「小さいAPIをサクッと作りたい」だけなら、Express、Hono、Fastifyの方が軽い

まず覚える3つ(超重要)

sequenceDiagram
    participant C as クライアント
    participant Ctrl as Controller
    participant Svc as Service
    participant DB as DB / 外部API

    C->>Ctrl: HTTP リクエスト
    Ctrl->>Svc: 処理を依頼
    Svc->>DB: データ取得・保存
    DB-->>Svc: 結果
    Svc-->>Ctrl: 処理結果
    Ctrl-->>C: HTTP レスポンス(JSON等)

1) Controller(入口)

HTTPリクエストを受け取って、レスポンスを返す場所。

  • 例: GET /users を受けて、ユーザー一覧JSONを返す
  • Controllerには「入力の受け取り・バリデーション・Service呼び出し」くらいを書き、重い処理はServiceに逃がすのが基本

2) Service(中身)

ビジネスロジックを書く場所。

  • 例: DBからユーザーを取る、整形する、権限をチェックする
  • Controllerから呼ばれる“部品”になりがちなので、単体テストしやすい

3) Module(配線)

ControllerやServiceを「このアプリはこれを使う」と登録して、つなぐ場所。

  • NestJSは基本的に「Moduleの世界」に閉じて動く
  • 何かが動かないとき、まず疑うのは「Moduleに登録されてない」「export/importの関係が間違ってる」

最小の形(Controller / Service / Module)

NestJSを早速試してみることを目的に、最小のREST APIを1本作る。

この記事で作るサンプル

nest new で作ったプロジェクト api-sample に、ユーザー一覧を返すだけの API を足す。

項目内容
エンドポイントGET /users のみ(作成・更新・削除はしない)
レスポンスユーザー配列の JSON(idname を持つオブジェクトが2件)
データの置き場所UsersService 内の固定配列(DB・ORM は使わない)
学ぶことUsersModule / UsersController / UsersService の役割分担と、Module への登録・DI

動作イメージは次のとおり。

curl http://localhost:3000/users
# → [{"id":"u_1","name":"Taro"},{"id":"u_2","name":"Hanako"}]

初期状態の GET /Hello World!)はそのまま残し、別ルートとして /users を追加する形になる。実務の「機能ごとに Module を分ける」最小版として読める。

サンプル実装手順

  1. 作業フォルダ作成
mkdir nestjs-practice
cd nestjs-practice
  1. NestJSプロジェクト作成(公式CLI)
npm i -g @nestjs/cli
nest new api-sample

Nest CLI(@nestjs/cli は、nest newnest g module などのコマンドを提供する公式のCLIツールである。npm i -g @nestjs/cli は、作業ディレクトリに関係なく nest を叩けるようにする グローバルインストール

nest new が作る api-sample/package.jsondevDependencies にも @nestjs/cli が入る。リポジトリを clone した人は npm install(または pnpm / yarn)後に npx nestpackage.json の scripts 経由で同じCLIを使えるため、グローバルに入れていなくてもよい。

初期のテスト雛形(例: src/app.controller.spec.tstest/app.e2e-spec.ts)も、この nest new のタイミングで置かれることが多い。テストの書き方は後続の NestJSテスト入門 で扱う。

nest new 実行時に package manager を聞かれたら、普段使っているもの(npm/pnpm/yarn)を選べばOK。
ここでは npm 想定で進める。

  1. プロジェクトへ移動して起動確認
cd api-sample
npm run start:dev

ブラウザで http://localhost:3000 にアクセスして Hello World! が出れば初期起動は成功。

  1. ファイル構成(今回触る場所)
api-sample/
└─ src/
   ├─ app.module.ts
   └─ users/
      ├─ users.module.ts
      ├─ users.controller.ts
      └─ users.service.ts

各ファイルの役割は次のとおり。

  • src/app.module.ts
    アプリ全体のルートModule。機能Module(今回は UsersModule)を読み込む起点。
  • src/users/users.module.ts
    users機能の配線定義。ControllerとServiceを登録して関連付ける。
  • src/users/users.controller.ts
    HTTPの入口。GET /users のようなルーティングを定義し、Serviceを呼び出す。
  • src/users/users.service.ts
    処理の中身(ビジネスロジック)を置く。DBアクセスや計算など、Controllerから分離したい処理を書く。

どこを変えると何が変わるかをざっくり言うと、

  • レスポンス内容を変える: users.service.ts
  • エンドポイントURLやHTTPメソッドを変える: users.controller.ts
  • users機能の依存関係を変える: users.module.ts
  • アプリに機能を追加/読み込みする: app.module.ts
  1. users モジュール一式を作成
# 別ターミナルで実行(start:dev を止めてもOK)
cd api-sample/src/
nest g module users
nest g service users
nest g controller users

各コマンドは、Nest CLI が 雛形ファイルの生成Module への登録(配線) をまとめて行う。

コマンド生成される主なファイル具体的にやっていること
nest g module userssrc/users/users.module.tsusers 機能用の Module を新規作成する。多くの場合、ルートの app.module.tsimportsUsersModule を追記して、アプリ全体から読み込めるようにする。
nest g service userssrc/users/users.service.ts処理本体用の Service クラスを生成し、UsersModuleproviders に登録する。@Injectable() 付きの空クラスができ、あとから DI で Controller などに注入できる。
nest g controller userssrc/users/users.controller.tsHTTP の入口となる Controller を生成し、UsersModulecontrollers に登録する。@Controller() 付きの空クラスができ、あとから @Get() などのルートを足す。

手で同じことをするなら、「3 ファイルを作る」+「users.module.ts で Controller / Service を imports・providers・controllers に書く」+「app.module.ts に UsersModule を足す」に相当する。CLI はその定型作業を省略してくれる。

  1. src/users/users.service.ts
import { Injectable } from "@nestjs/common";

type User = { id: string; name: string };

@Injectable()
export class UsersService {
  list(): User[] {
    return [
      { id: "u_1", name: "Taro" },
      { id: "u_2", name: "Hanako" },
    ];
  }
}

このファイルは「返すデータの中身」を決める場所。
今は固定配列を返しているが、実務ではここがDBや外部API呼び出しに置き換わることが多い。

  1. src/users/users.controller.ts
import { Controller, Get } from "@nestjs/common";
import { UsersService } from "./users.service";

@Controller("users")
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get()
  list() {
    return this.usersService.list();
  }
}

このファイルはHTTPルーティングの定義場所。

  • @Controller("users") でベースパスを /users にする
  • @Get() で GET リクエストを受ける
  • メソッド内で usersService.list() を呼び、結果をJSONで返す
  1. src/users/users.module.ts(生成済みだが確認)
import { Module } from "@nestjs/common";
import { UsersController } from "./users.controller";
import { UsersService } from "./users.service";

@Module({
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule {}

このファイルはDIの登録表のイメージ。

  • controllers: HTTP入口として使うクラス
  • providers: Service など、Nest に生成・管理させる部品の一覧

@Injectable() や「注入」の意味は、手順のあとにある Aside「DI(依存注入)が何をしているか」で整理する。

  1. src/app.module.tsUsersModule を読み込む
import { Module } from "@nestjs/common";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";
import { UsersModule } from "./users/users.module";

@Module({
  imports: [UsersModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

imports: [UsersModule] がないと、users機能はアプリに組み込まれない。
「ファイルはあるのにルートが効かない」ときはここを疑うとよい。

  1. 動作確認
npm run start:dev
curl http://localhost:3000/users

以下のようなJSONが返れば成功。

[
  { "id": "u_1", "name": "Taro" },
  { "id": "u_2", "name": "Hanako" }
]

関連

参考

NestJS公式サイト