← コース一覧
NestJS
Node.js でサーバーサイドアプリケーションを構築するためのフレームワーク NestJS を学ぶコースです。1レッスン1テーマの小さなステップで、公式ドキュメントの流れに沿って保守しやすい API の作り方を身につけます。
公式ドキュメント: NestJS Documentation ↗基礎
- 1NestJSとはNestJS は Node.js 上で動くサーバーサイドフレームワークで、アプリを「コントローラ」「プロバイダ」「モジュール」という3種類の部品に分けて組み立てます。TypeScript を前提に設計されており、部品の種類と置き場所をフレームワーク側が決めてくれるのが特徴です。
- 2main.ts とブートストラップNestJS アプリのエントリーポイントは main.ts です。NestFactory.create() にルートモジュール(AppModule)を渡してアプリのインスタンスを作り、app.listen() でポートを指定して HTTP サーバーを起動します。この一連の起動処理をブートストラップと呼びます。
- 3コントローラと @Getコントローラは HTTP リクエストを最初に受け取る部品です。クラスに @Controller("users") を付けると /users 配下のパスを担当し、メソッドに @Get() を付けるとそのメソッドが GET リクエストの処理(ハンドラ)になります。POST なら @Post() のように、HTTP メソッドごとにデコレータが用意されています。
- 4ルートパラメータと @ParamGET /users/123 のように URL の一部が可変になる場合は、@Get(":id") とコロン付きでパスを宣言します。ハンドラの引数に @Param("id") を付けると、その位置の値を受け取れます。
- 5リクエストボディと @BodyPOST や PUT で送られてくる JSON ボディは、ハンドラの引数に @Body() を付けて受け取ります。NestJS が JSON のパースを済ませてくれるので、引数にはオブジェクトがそのまま入ってきます。
DIとモジュール
- 6実行可DIの考え方DI(依存性の注入)とは、クラスが必要とする別のクラスを自分で new するのではなく、外部から渡してもらう設計です。NestJS の中心にはこの仕組みを自動化する「DIコンテナ」があり、クラスの生成と受け渡しをフレームワークが肩代わりします。
- 7プロバイダとサービスプロバイダは DIコンテナに登録してアプリ内で注入できるようにしたクラスの総称で、その代表がビジネスロジックを担う「サービス」です。クラスに @Injectable() を付けると注入可能になり、利用側はコンストラクタの引数に型を書くだけで受け取れます。
- 8モジュール — 機能のまとまりモジュールは、関連するコントローラとプロバイダを1つの機能単位に束ねる部品です。@Module() デコレータに controllers と providers を列挙して登録します。アプリ全体は必ず1つのルートモジュール(AppModule)から始まり、機能モジュールを imports でぶら下げていきます。
- 9モジュール間の共有 — exports と importsモジュール内の providers は、そのままでは同じモジュールの中からしか注入できません。他のモジュールにサービスを使わせたいときは、提供側が exports にそのプロバイダを載せ、利用側が imports でモジュールごと取り込みます。
リクエスト処理パイプライン
- 10DTO — リクエストの形を定義するDTO(Data Transfer Object)は、リクエストボディの形を表す専用クラスです。CreateUserDto のように「操作名 + Dto」という名前でクラスを作り、@Body() の型注釈に使います。インラインの型注釈と違い、ボディの形に名前が付いて再利用できるようになります。
- 11ValidationPipe — 入力の自動検証ValidationPipe は、DTO に書いた検証ルールを自動で実行する仕組みです。class-validator の @IsString() や @Min() などのデコレータを DTO のプロパティに付け、main.ts で app.useGlobalPipes(new ValidationPipe()) を設定すると、@Body() で受け取る値が届く前に検証されます。
- 12実行可カスタムパイプ — transform の契約パイプの正体は、PipeTransform インターフェースの transform() メソッドを実装したクラスです。「ハンドラに渡る直前の値を受け取り、変換した値を返す。不正なら例外を投げる」というのがパイプの契約のすべてで、ValidationPipe もこの契約に従う1つの実装にすぎません。
- 13ガード — 認証・認可の入口ガードは「このリクエストを処理してよいか」をハンドラ実行前に判定する部品です。CanActivate インターフェースを実装したクラスを作り、canActivate() で true を返せば通過、false を返せば 403 Forbidden で拒否されます。適用は @UseGuards() デコレータで行います。
- 14インターセプター — 処理の前後に割り込むインターセプターは、ハンドラの実行を前後から挟み込む部品です。NestInterceptor インターフェースの intercept() を実装し、next.handle() を呼ぶとハンドラ本体が実行されます。その呼び出しの前後にコードを置けるため、「実行前の準備」と「実行後の加工」を1か所に書けます。
- 15ミドルウェア — ルーティング前の共通処理ミドルウェアは、どのハンドラが処理するか決まるより前にリクエストへ触れる部品です。NestMiddleware の use(req, res, next) を実装し、最後に next() を呼んで処理を先へ渡します。適用はモジュールの configure() で、どのパスに効かせるかを指定します。
- 16例外フィルタ — エラーレスポンスの統一例外フィルタは、処理中に投げられた例外を捕まえてエラーレスポンスの形を決める部品です。@Catch(HttpException) を付けたクラスで ExceptionFilter を実装し、catch() の中でステータスコードとレスポンスボディを組み立てます。
実務テクニック
- 17環境変数と ConfigModuleDB の接続先や API キーのような環境ごとに変わる値は、コードに直書きせず環境変数で渡します。NestJS では @nestjs/config パッケージの ConfigModule.forRoot() を AppModule に登録すると .env ファイルが読み込まれ、ConfigService を注入して config.get("KEY") で値を取り出せます。
- 18実行可リクエストのライフサイクル全体像ここまで学んだ部品は、1つのリクエストに対して決まった順序で実行されます。大きな流れは「ミドルウェア → ガード → インターセプター(前) → パイプ → ハンドラ → インターセプター(後)」で、途中で投げられた例外は例外フィルタが受け止めます。
セキュリティと認証
- 19パスワードのハッシュ化 — bcryptパスワードを平文のまま DB に保存するのは厳禁です。漏えいした瞬間に全ユーザーのパスワードが読めてしまうためです。代わりに bcrypt のようなハッシュ関数で不可逆な文字列に変換して保存し、ログイン時は入力値を同じ関数にかけて照合します。NestJS では bcrypt パッケージの hash() で保存用の値を作り、compare() で照合します。
- 20認証の流れ — Passportと戦略認証とは「あなたは誰か」を確かめる処理です。典型的なログインの流れは、クライアントが送ったメールアドレスとパスワードをサービスで検証し、正しければ本人であることを示すトークンを発行して返す、というものです。以降のリクエストではそのトークンを提示してもらい、毎回ログインし直さずに本人だと判断します。
- 21実行可JWT — トークンによる認証JWT(JSON Web Token)は、認証情報を1つの文字列に詰め込んで持ち運べる仕組みです。トークンは header.payload.signature の3部構成で、payload にユーザー ID や権限を入れ、signature はサーバーの秘密鍵で作った署名です。NestJS では @nestjs/jwt の JwtModule を登録し、jwtService.sign() で発行、jwtService.verify() で検証します。
- 22実行可認可とロール — RBAC認可は「この人が何をしてよいか」を判定する処理で、RBAC(Role-Based Access Control)はユーザーに付けたロール(admin、editor など)で許可を決める代表的な方式です。NestJS では @Roles("admin") のようなカスタムデコレータでハンドラに必要なロールを宣言し、既習のガードがそのメタデータとログインユーザーのロールを突き合わせて通過か拒否かを決めます。
- 23CORS と Helmetブラウザには同一オリジンポリシーという制約があり、あるオリジン(例: https://app.example.com)で読み込んだページから別オリジンの API を呼ぶことは既定で制限されます。フロントエンドと API のドメインが異なる構成では、API 側が「このオリジンからのアクセスを許可する」と明示する必要があり、これが CORS です。NestJS では main.ts で app.enableCors() を呼ぶだけで有効になり、許可するオリジンを指定できます。
- 24レート制限 — Throttlerレート制限は、一定時間内に受け付けるリクエスト数に上限を設ける仕組みです。NestJS では @nestjs/throttler の ThrottlerModule を AppModule に登録し、ttl(時間枠)と limit(その間に許す回数)を設定します。上限を超えたクライアントには自動で 429 Too Many Requests が返ります。
データベース
- 25データベース接続 — TypeOrmModuleNestJS からデータベースを使うには、まず接続設定を1か所にまとめます。TypeORM を使う場合は TypeOrmModule.forRoot() を AppModule の imports に登録し、DB の種類(type)・ホスト・認証情報・対象とするエンティティ(entities)などを渡します。これは既習の ConfigModule.forRoot() と同じ「モジュールに設定を流し込んで初期化する」パターンで、ORM が違っても接続設定を一元化する考え方は共通です。
- 26エンティティ — テーブルを表すクラスエンティティは、データベースの1つのテーブルを表す TypeScript のクラスです。クラスに @Entity() を付けるとテーブルに対応し、各プロパティに @Column() を付けると列に対応します。主キーには @PrimaryGeneratedColumn() を付けると、自動採番される id 列になります。「クラス = テーブル、プロパティ = 列」という対応関係は、TypeORM に限らず多くの ORM に共通する基本発想です。
- 27リポジトリでCRUDリポジトリは、1つのエンティティに対する読み書き(CRUD)をまとめて担当するオブジェクトです。TypeOrmModule.forFeature([User]) でそのエンティティのリポジトリを使えるようにし、サービスのコンストラクタで @InjectRepository(User) を付けて注入します。これは既習の DI の一種で、自分で new せずフレームワークからリポジトリを受け取る点は通常のサービス注入と同じです。
- 28実行可リレーション — テーブル間の関連リレーションは、テーブル同士のつながりを表す仕組みです。たとえば「1人のユーザーが複数の投稿を持つ」関係は、User 側に @OneToMany、Post 側に @ManyToOne を付けて表現します。「多」の側(Post)には相手の主キーを指す外部キー列(userId)ができ、これがどの行とどの行が結びつくかを保持します。1対多・多対1という関連の考え方は ORM が違っても共通です。
- 29実行可トランザクショントランザクションは、複数の更新を「全部成功」か「全部失敗」のどちらかにまとめる仕組みです。代表例は送金で、口座Aから引く更新と口座Bに足す更新は、両方成功しなければ片方だけ反映されて残高がおかしくなります。途中で失敗したら開始時点まで巻き戻す(ロールバックする)ことで、データの一貫性を守ります。この All or Nothing の考え方は ORM や DB が違っても共通です。
DIの応用とテスト
- 30カスタムデコレータ — パラメータデコレータを自作するNestJS では createParamDecorator を使って、@Param や @Body のような引数デコレータを自作できます。createParamDecorator に (data, ctx) => ... のファクトリ関数を渡すとデコレータが作られ、ctx.switchToHttp().getRequest() でリクエストを取り出して、必要な値を返すように書きます。代表例は、認証ガードが request.user に入れた値を取り出す @User() です。
- 31実行可カスタムプロバイダ — useValue / useFactory / useClassこれまでは providers にクラスを並べるだけでしたが、これは { provide: UsersService, useClass: UsersService } の省略形です。providers には provide(トークン)と提供方法の組を明示でき、固定値を渡す useValue、関数で値を組み立てる useFactory、別クラスに差し替える useClass を使い分けられます。トークンには文字列も使えるので、注入側では @Inject("トークン名") で受け取ります。
- 32実行可インジェクションスコープ — Singleton / Request / Transientプロバイダの既定のスコープはシングルトンで、アプリ起動時に一度だけ生成されたインスタンスを全リクエストで共有します。これを変えたいときは @Injectable({ scope: Scope.REQUEST }) のように指定します。Scope.REQUEST はリクエストごとに新しいインスタンスを作り、Scope.TRANSIENT は注入されるたびに新しいインスタンスを作ります。
- 33動的モジュール — forRoot パターンここまで使ってきた ConfigModule.forRoot(...) や TypeOrmModule.forRoot(...) は、動的モジュールという仕組みです。通常のモジュールは @Module({...}) で構成が固定ですが、動的モジュールは forRoot(options) という静的メソッドを持ち、呼び出し側から設定を受け取って、その場で providers を組み立てた DynamicModule オブジェクト({ module, providers, exports }) を返します。
- 34ユニットテスト — Test.createTestingModuleNestJS は @nestjs/testing でテスト用の DIコンテナを提供します。Test.createTestingModule({ providers: [...] }) でテスト専用のモジュールを組み立て、.compile() した後 module.get(UsersService) で対象のインスタンスを取り出します。テストランナーには Jest を使うのが既定で、describe / it / expect でアサーションを書きます。