NestJS Deep Dive 2026 ─ 内部構造・再発明回避・パフォーマンスを 10 章で読み解く
NestJS をプロダクションで使っているが内部までは把握していないエンジニアに向けて、DI コンテナの動き・標準モジュール 18 種・再発明 TOP 10・アンチパターン 15・2026 構成パターンを 10 章で深掘りする
はじめに ─ 使い倒す前に知るべき 4 つの原理
こんな疑念を、毎日感じていないか
// あなたが何気なく書いているこのコード
@Injectable()
export class CacheService {
private cache = new Map<string, { value: any; expiresAt: number }>();
constructor() {
setInterval(() => this.cleanup(), 60_000); // GC を自前で
}
get(key: string) { /* ... */ }
set(key: string, value: any, ttl: number) { /* ... */ }
private cleanup() { /* ... */ }
}
これ、CacheModule で 3 行だ。さらに Redis にも切り替えられて、@CacheKey / @CacheTTL / CacheInterceptor で宣言的に書ける。「わざわざ自前で書いてる」と気づかないまま、似たようなコードが社内に何十個も育っていく ─ それが NestJS あるあるだ。
別の例:
// scope を使えば「リクエストごとに新しいインスタンス」が手に入る、便利
@Injectable({ scope: Scope.REQUEST })
export class TenantContextService {
constructor(@Inject(REQUEST) private req: Request) {}
get tenantId() { return this.req.headers['x-tenant-id']; }
}
// このサービスを使う Service / Controller も全部 REQUEST スコープになる
// = 30,000 並行リクエストで 30,000 個のインスタンスが毎回生成される
// = レイテンシが約 5% 増える、heap が膨らむ
これは NestJS 公式が “bubble-up” と呼ぶ伝染現象。「便利だから」と REQUEST スコープを足したつもりが、依存元方向に勝手に広がっていって、いつの間にか全エンドポイントが重くなる。
「使い倒してないと気づけない」が積もる理由
NestJS は強い意見を持ったフレームワークだ。Spring 系の Java、ASP.NET Core、Angular 由来の DI とデコレータ、RxJS 思想 ─ これらが正しい使い方として暗黙に前提されている。表面的な使い方(Controller / Service / Module)だけで動いてしまうから、深さに気づくきっかけがない。
その結果、よくある現象:
- 再発明:CacheModule、ScheduleModule、TerminusModule、ConfigModule、ThrottlerModule、EventEmitterModule、BullMQ … などが提供している機能を、自前で書いている
- scope 伝染:気軽に
Scope.REQUESTを使ったらレイテンシが 5% 落ちている - Logger 不調:標準 Logger が同期 stdout 書き込みでホットパスをブロックしている
- Validation コスト:
class-validatorがプロダクトの最大の CPU bottleneck になっている(Samchon の実測で typia 比 15,000倍遅い) - アンチパターン:Interceptor で DB を引いている / Pipe で外部 API を叩いている / Service 内に
try-catchを散らかしている
これらはドキュメントに書いていないわけではない。書いてあるが、通読しないと点と点が線にならない。
本シリーズはこの「点と点を線でつなぐ」を目的とする。
2026年5月時点 ─ NestJS の現在地
数字で見る:
| 指標 | 数字 |
|---|---|
| NestJS v11 リリース | 2025-01-20(Kamil Myśliwiec) |
| Node.js 要件 | v20+ 必須(v18 は EOL 2025-04) |
| デフォルト HTTP アダプタ | Express v5(本記事の前提) |
| TypeScript Decorator | Stage 3 標準化進行中、しかし NestJS は依然 experimentalDecorators + emitDecoratorMetadata |
| 大きな変更 | Module 解決アルゴリズム刷新(オブジェクト参照ベース)/ ライフサイクルフック順序が対称的に逆転 |
| v12 ロードマップ(参考) | ESM 完全移行 / Vitest / Rspack / Standard Schema 対応(Q3 2026 予定) |
| Validation 性能の現実 | class-validator は typia 比で最大 15,000倍遅い(Samchon 実測) |
| Cache 周りの破壊的変更 | cache-manager v6 で TTL 単位が秒 → ミリ秒に変更(事故多発) |
| BullMQ の現状 | @nestjs/bull は maintenance mode、新規は @nestjs/bullmq 推奨 |
| LLM/Agent backend 用途 | SSE / @nestjs-mcp/server / @rekog/mcp-nest 登場 |
v11 で起こった変更を理解しないと、踏むはずのなかった地雷を踏む。本シリーズは v11 + Express v5 + Node.js 20 を前提に書く。
このシリーズが目指すこと
読み終えたとき、あなたは以下のことができるようになる。
内部構造の理解
- DI コンテナの動作(DependenciesScanner → InstanceLoader、
loadPrototype+loadInstanceの2フェーズ)を語れる - Provider のスコープ(DEFAULT / REQUEST / TRANSIENT)と REQUEST scope の bubble-up を理解する
- Decorator の二層構造(メタデータ宣言 + 実行時 hook)を Reflect.metadata から説明できる
- リクエストライフサイクルの 6 つの層を正しい順序で並べられる
- v11 の主要変更点(Module 解決 / フック順序逆転 / Express v5)を実装で活かせる
再発明していないかの棚卸し
- 標準モジュール 18 種を網羅的に把握する
- 再発明されがちな機能 TOP 10(pagination / retry / circuit breaker / lock / audit / tenant context / request-scoped logger / idempotency 等)を識別できる
- 自前実装を 準公式パッケージ(nestjs-pino / nestjs-cls / nestjs-zod / nestjs-paginate / nestjs-resilience / murlock 等)に寄せられる
パフォーマンス・メモリ効率の改善
- DI scope 伝染を Durable Providers で救済する設計
- Validation コストを class-validator → Zod / Typia へ段階移行
- Logger を Pino + AsyncLocalStorage に切り替える
- Cache Stampede 対策(TTL Jitter / 分散ロック / Stale-While-Revalidate)を実装できる
実践のアンチパターン回避
- 代表的アンチパターン 15 個を「症状 → 根本原因 → 脱出法」の3段で識別できる
- LLM/Agent backend、MCP Server、CQRS、Modular Monolith など 2026 構成パターンを取り入れられる
- Graceful Shutdown を K8s 環境で正しく動かせる
4 つの根本原理 ─ 全章で繰り返し参照する伏線
NestJS の機能は表面的にはたくさんあるが、その裏には4つのシンプルな原理が一貫して流れている。各章末で「この章はどの原理に立脚しているか」を明示し、最終章で全部回収する。
| # | 原理 | ひと言で |
|---|---|---|
| 1 | DI コンテナを信頼する | スコープ・ライフサイクル・循環参照解決の3点セット。「自分で new していないか」「DI で取れるものを取っているか」が出発点 |
| 2 | Decorator は「メタデータ宣言 + 実行時 hook」の二層 | Reflect.metadata で動く構造を理解すれば、Custom Decorator もリフレクションも怖くない |
| 3 | リクエスト境界の「層」を正しく使い分ける | Middleware → Guard → Interceptor → Pipe → Handler → Filter の順序が責務とパフォーマンスを決める |
| 4 | メモリ効率は DI スコープ × ストリーム × Logger に集約される | アンチパターン 15 のうち大半はこの3軸のいずれか |
これら4つは、Custom Decorator を書くときも、Cache 設計をするときも、本番障害の対応をするときも、まったく同じ式として登場する。
対象読者
- NestJS をプロダクションで使っているバックエンドエンジニア / テックリード / アーキテクト
- Controller / Service / Module / Pipe / Guard / Interceptor の基本は習得済み
- 「この機能、自分で書いてるけど NestJS の何かと被ってないか?」という疑念を持っている
- パフォーマンス・メモリ効率の改善余地に気づいている
- 表層的な「Hello World」記事は卒業した
- 前提:TypeScript の Decorator・Generics・Type Inference を理解、Node.js のイベントループは知っている
入門者向けではない。プロダクションで動いている NestJS アプリを抱えている人が、その質を一段引き上げるための本。
メタ情報
| 項目 | 内容 |
|---|---|
| 難易度 | ★★★★☆(中〜上級者向け、deep dive) |
| 想定読了時間 | 約 130〜150 分(コードと図を読みながら) |
| 対象環境 | NestJS v11(2025-01-20 リリース) / Express v5 / Node.js 20+ / TypeScript 5.x |
| 対象時期 | 2026 年 5 月時点。v12 ロードマップを最終章で言及 |
| 章数 | 全 10 章 + おわりに |
| 図解 | mermaid + SVG インフォグラフィック多めの方針 |
| HTTP アダプタ | Express デフォルト前提(Fastify への移行は扱わない) |
シリーズ構成
第0章 (本ファイル): はじめに ─ 使い倒す前に知るべき 4 つの原理
第1部: 内部構造を理解する
第1章: NestJS の心臓 ─ DI コンテナの内部動作
第2章: Decorator と Reflect.metadata ─ 二層構造を読み解く
第3章: リクエストライフサイクル ─ 6 つの層を正しく使い分ける
第4章: Module システムと v11 の変更点
第2部: 再発明していないか棚卸しする
第5章: NestJS が提供する 18 のモジュール
第6章: 再発明 TOP 10 ─ 自前実装をやめて準公式に寄せる
第3部: パフォーマンス・メモリ効率を上げる
第7章: DI スコープの罠 ─ Request bubble-up と Durable Providers
第8章: Validation / Logger / Cache のコスト ─ 静かに重い 3 層
第4部: 実践
第9章: アンチパターン 15 ─ 症状 → 根本原因 → 脱出法
最終章
第10章: おわりに ─ 4 原理回収 + 2026 構成パターン + v12 ロードマップ
第1部から順に読むと、内部構造 → 棚卸し → 性能改善 → アンチパターン → 構成パターンの流れで理解が深まる。気になる章だけ拾い読みすることもできるが、4 つの根本原理は第1部で導入したあと第3部以降で再利用するため、最初に第0章 → 第1部を通すことを勧める。
本書のスタイル
- コードと図を多用:mermaid で構造、コードで挙動を示す
- v11 の挙動を正確に:機能 / API は v11.x の現行仕様で書く
- アンチパターンは 3 段:症状 → 根本原因 → 脱出法 で揃える
- ✅ 良い設計 / ❌ 悪い設計 の対比を多用
- 数字を具体的に:「重い」ではなく「typia 比 15,000倍遅い」、「速い」ではなく「インデキシング 1/1000」
- 引用元は本文中にリンク、最終章にまとめて再掲
それでは、まずは NestJS の心臓部 ─ DI コンテナの中で何が起きているか ─ から見ていこう。
目次
- NestJS の心臓 ─ DI コンテナの内部動作 NestJS 起動時に DependenciesScanner と InstanceLoader が何をしているのか、Provider Scope の3種類、Custom Provider の4種、循環依存の解決メカニズム、ModuleRef まで、DI コンテナの全体を分解する
- Decorator と Reflect.metadata ─ 二層構造を読み解く NestJS の Decorator が「メタデータ宣言 + 実行時 hook」の二層で動く仕組みを、Reflect.metadata の格納場所、design:paramtypes、TypeScript Stage 3 を採用しない理由まで掘り下げる
- リクエストライフサイクル ─ 6 つの層を正しく使い分ける Middleware → Guard → Interceptor → Pipe → Handler → Filter の完全順序、各層の責務、global / controller / route のスコープ違い、ExecutionContext の正しい使い方を分解する
- Module システムと v11 の変更点 Module DAG・Dynamic Module・Lazy Loading の制限・Global Module、そして v11 で起きた Module 解決アルゴリズム刷新とライフサイクルフック順序の対称的逆転を分解する
- NestJS が提供する 18 のモジュール NestJS の標準モジュール 18 種を網羅的に整理し、各モジュールで「再発明されがちな機能」を明示する。「自分で書いてるそれ、NestJS の何か」を即座に判定するための地図
- 再発明 TOP 10 ─ 自前実装をやめて準公式に寄せる NestJS 公式モジュールではカバーされていない領域 ─ pagination、retry、circuit breaker、distributed lock、tenant context、request-scoped logger、idempotency 等 ─ を、準公式パッケージで埋める TOP 10 マップ
- DI スコープの罠 ─ Request bubble-up と Durable Providers REQUEST スコープの伝染(bubble-up)が引き起こすパフォーマンス劣化を実体で示し、Durable Providers と nestjs-cls(AsyncLocalStorage)の使い分けを設計指針として提示する
- Validation / Logger / Cache のコスト ─ 静かに重い 3 層 class-validator vs typia vs Zod の性能差(最大 15,000 倍)、Pino + AsyncLocalStorage の正しい統合、Cache Stampede 対策(TTL Jitter / 分散ロック / Stale-While-Revalidate)を分解する
- アンチパターン 15 ─ 症状 → 根本原因 → 脱出法 NestJS プロダクションで最もよく踏む 15 のアンチパターンを「症状 → 根本原因 → 脱出法」の3段で整理、コード Before/After で識別と修正方法を示す
- おわりに ─ 4 原理回収 + 2026 構成パターン + v12 ロードマップ 4 原理を全章にわたって回収し、2026 構成パターン(LLM/Agent backend、MCP Server、CQRS、Modular Monolith、Graceful Shutdown)と v12 ロードマップを整理する