Open6

NestJSを概観する

skgutskgut

参考

https://docs.nestjs.com/
https://zenn.dev/morinokami/articles/nestjs-overview
【2024年最新】NestJS入門 TypeScriptではじめるサーバーサイド開発

NestJSとは

Node.jsサーバーサイドアプリケーションを構築するためのフレームワーク。
JavaScriptでサーバサイドアプリケーションを作成するためのアーキテクチャを提供する、という哲学のもと構成されている。

公式ドキュメントのOverviewには、以下の概念が項目として挙げられている。

  • Controllers
  • Providers
  • Modules
  • Middleware
  • Exception filters
  • Pipes
  • Guards
  • Interceptors
  • Custom Decorator

このうち、Controllers, Providers, Modulesはクライアントからのリクエストに対するルーティングやビジネスロジック、またそれらをまとめる機能を提供する。Middleware, Exception filters, Pipes, Guards, Interceptorsはリクエストとレスポンスの経路上で様々な役割を果たす。

また、Middleware を除く

  • Exception filters
  • Pipes
  • Guards
  • Interceptors

について、これらをアプリケーションへと登録する際に、実際には次の四つのレベルがある:

  • Global (グローバルなレベル)
  • Controller (コントローラのレベル)
  • Method (メソッドのレベル)
  • Param (パラメータのレベル、これは後述するように Pipe のみ設定可能)

つまり、どのレベルで各機能を使いたいかに応じて、コード内での使用方法も変化する。

skgutskgut

Controllers, Providers, Modules

これらはNestJSのアーキテクチャの基本をなす概念であり、このアーキテクチャを図にすると以下のようになる。

Featureはルートモジュールに登録しないと使えない。
Feature実装→ルートモジュール追加という流れ

skgutskgut

Moduleとは

ControllerやServiceをまとめ、1つの機能として利用できるセットとして登録する役割を持つもの。
アプリケーションには必ず1つのルートモジュールと0個以上のFeatureモジュールがあるが、コードの整理のためにFeatureモジュールを用いることが強く推奨されている。

定義方法

  1. クラスに@Moduleデコレーターをつける。
  2. @Moduleデコレーターの各プロパティを記述する。
    • providers
      DIをするためのプロパティ。@Injectableデコレーターがついたクラスを記述する。
    • controllers
      Controllerを使用するためのプロパティ。@Controllerデコレーターがついたクラスを記述する。
    • imports
      モジュール内部で必要な外部モジュールを記述するためのプロパティ。
      Featureモジュールをルートモジュールに追加する場合もここを利用する。
    • exports
      外部モジュールで利用したいものを記述する。
cats/cats.module.ts
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
})
export class CatsModule {}
app.module.ts
import { Module } from '@nestjs/common';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule],
})
export class AppModule {}

作成コマンド

nest g module <name>

これを実行することで、Featureモジュールの作成に加えルートモジュールにFeatureモジュールが追加
される。

skgutskgut

Controllerとは

クライアントからのリクエストを受け取り、レスポンスを返す役割をもつ。つまり、NestJSにおけるルーティングの機能を担う。

定義方法

  1. クラスに@Controller()デコレータをつける。
  2. メソッドにHTTPメソッドデコレーターをつける。
cats.controller.ts
import { Controller, Get, Query, Post, Body, Put, Param, Delete } from '@nestjs/common';
import { CreateCatDto, UpdateCatDto, ListAllEntities } from './dto';

@Controller('cats')
export class CatsController {
  @Post()
  create(@Body() createCatDto: CreateCatDto) {
    return 'This action adds a new cat';
  }

  @Get()
  findAll(@Query() query: ListAllEntities) {
    return `This action returns all cats (limit: ${query.limit} items)`;
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return `This action returns a #${id} cat`;
  }

  @Put(':id')
  update(@Param('id') id: string, @Body() updateCatDto: UpdateCatDto) {
    return `This action updates a #${id} cat`;
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return `This action removes a #${id} cat`;
  }
}

作成コマンド

nest g controller <name>

これを実行することで、コントローラーの作成だけでなく関連するFeatureモジュールにコントローラーが登録される。

cats/cats.module.ts
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  controllers: [CatsController], // Controller の登録
  providers: [CatsService],
})
export class CatsModule {}
skgutskgut

Serviceとは

Providerの一種で、ひとまとまりの処理。Controllerから呼び出すことで機能を実現する。

Provider

@Injectable() デコレータを適用したクラスで、依存関係として注入(Dependency Injection)される役割を持つ単純なJavaScriptのクラス。
Controllerから複雑なタスクを委任され、代わりに実行することでコードの見通しをよく保つことができる。DIの機能はNestJSのランタイムに委任でき、開発者は、NestJSから渡されたProviderインスタンスを使うだけで目的の機能を使うことができる。

定義方法

  1. クラスに@Injectable()デコレーターをつける。
  2. メソッドを作成する。

使用方法

  1. ModuleのprovidersにServiceを登録する。
  2. ControllerのconstructorでServiceを引数に取る
cat.service.ts
import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface';

@Injectable()
export class CatsService {
  private readonly cats: Cat[] = [];

  create(cat: Cat) {
    this.cats.push(cat);
  }

  findAll(): Cat[] {
    return this.cats;
  }
}
cat.controller.ts

import { Controller, Get, Post, Body } from '@nestjs/common';
import { CreateCatDto } from './dto/create-cat.dto';
import { CatsService } from './cats.service';
import { Cat } from './interfaces/cat.interface';

@Controller('cats')
export class CatsController {
  constructor(private catsService: CatsService) {}

  @Post()
  async create(@Body() createCatDto: CreateCatDto) {
    this.catsService.create(createCatDto);
  }

  @Get()
  async findAll(): Promise<Cat[]> {
    return this.catsService.findAll();
  }
}

作成コマンド

nest g service <name>

これを実行することで、Serviceが作成されるだけでなく、関連するFeatureモジュールにServiceが登録される。

skgutskgut

Pipe

クライアントから送られたリクエストに対し、ハンドラで受け取る前にある処理を施す。
行われる処理は以下の2つ。

  • 変換
    入力データを希望の形式に変換する(例:文字列から整数へ)
  • バリデーション
    入力データを評価し、有効であればそのまま通過させ、そうでなければ例外を投げる。

イメージとしては以下の通り。
Pipe
引用:https://docs.nestjs.com/pipes

@Injectable()デコレーターを適用し、PipeTransformインターフェースを実装したクラスとして定義されるが、組み込みPipeやclass-validatorなど実装済のものを使用することが多い。

なお、NestJSには9つの組み込みPipeが存在する:

  • ValidationPipe
  • ParseIntPipe
  • ParseFloatPipe
  • ParseBoolPipe
  • ParseArrayPipe
  • ParseUUIDPipe
  • ParseEnumPipe
  • DefaultValuePipe
  • ParseFilePipe

なお、これらは@nestjs/commonから提供される。

適用方法

適用方法が複数ある。
a. ハンドラーへ適用


b. パラメータごとに適用

@Get(':id')
async findOne(@Param('id', ParseIntPipe) id: number) {
  return this.catsService.findOne(id);
}

c. グローバルへの適用