目次
- 目次
- 概要
- DTO(Data Transfer Object)とは
- DTOを使ってcreateメソッドをリファクタリングしていく
- NestJSにおけるバリデーションとは
- Pipeとは
- クラスバリデータとは
- NestJSにおける例外処理
- 終わり
- 参考記事
概要
この記事では、NestJSのバリデーションと例外処理についてまとめました。
DTO(Data Transfer Object)とは
DTOとは、データの受け渡しに使われるオブジェクトのことです。 NestJSでは、リクエストのデータを受け取るときに主に使います。
DTOを使う3つのメリット
- メンテナンス性が高まる
データの内容や型などが変更になった場合でも、修正箇所をDTO内に閉じ込めることができます。 - 安全性が高まる
やりとりするデータをDTOの型に制限することができるので、誤ったデータが扱われるリスクが減ります。 - NestJSのバリデーション機能が使える
型チェックだけでなく、複雑なバリデーションも可能です。NestJSのバリデーションは、型チェックだけではなく、入力の長さチェックなど、色々あります(これはRailsでも同じ)。
DTOを使ってcreateメソッドをリファクタリングしていく
DTOの作成方法
以下の手順でDTOを作成します。
- モジュールのディレクトリ直下にdtoディレクトリを作ります。
- 「メソッド名-単数形のモジュール名.dto」というファイル名のTSファイルを作ります。
- クラスでDTOを定義します(クラスバリデーターを使用するために、DTOはインターフェースではなく、クラスで定義します)。
例) create-item.dto.ts
import { Type } from 'class-transformer'; import { IsNotEmpty, IsInt, IsString, MaxLength, Min } from 'class-validator'; export class CreateItemDto { //プロパティはCreateリクエストのBodyパラメータと一緒 // 以下のデコレータは、クラスバリデータから提供されるデコレータ @IsString() @IsNotEmpty() @MaxLength(40) name: string; @IsInt() @Min(1) @Type(() => Number) price: number; @IsString() @IsNotEmpty() description: string; }
DTOを使うことで、@Bodyデコレータの記述をシンプルに書くことができます。
Before
↓ items.controller.ts
// POSTメソッドのハンドラを作成する @Post() create( @Body('id') id: string, @Body('name') name: string, @Body('price') price: number, @Body('description') description: string, ): Item { const item = { id, name, price, description, status: ItemStatus.ON_SALE, }; return this.itemsService.create(item); }
After
↓ items.controller.ts
// POSTメソッドのハンドラを作成する // BodyパラメータとDTOのプロパティが等しい場合、これらをまとめて一つのボディデコレータで受け取ることができます。 // DTOを使用する場合、Bodyデコレータに引数を与える必要はなく、DTO型の変数を定義すれば、その中に代入されます。 @Post() create(@Body() createItemDto: CreateItemDto): Item { return this.itemsService.create(createItemDto); }
↓ items.service.ts
create(createItemDto: CreateItemDto): Item { const item = { ...createItemDto, status: ItemStatus.ON_SALE, }; this.items.push(item); return item; }
NestJSにおけるバリデーションとは
NestJSにおけるバリデーションとは、リクエストオブジェクトの形式チェックを意味します。
ex)
- ユーザー名は1文字以上20文字以内
- emailはメールアドレスの形式
- パスワードは英数字で8文字以上
NestJSでバリデーションを行う方法
NestJSでバリデーションを行う場合、Pipe(パイプ)という機能を使います。
Pipeとは
Pipeの主な役目は、ハンドラがリクエストを受け取る前に、リクエストに対して処理を行うことです。 Pipeを使うことで、リクエストデータの変換とバリデーションが可能になります。
変換やバリデーションの処理を行った後のデータをハンドラに渡します。また、処理の中で例外を返すことも可能です。
NestJSの組み込みパイプ
NestJSには、便利な組み込みパイプが用意されています。
- ValidationPipe(バリデーションを行うためのバイプ)
↓ 変換のためのパイプ
- ParseIntPipe(入力を整数型に変換)
- ParseBoolPipe(入力をBoolean型に変換)
- ParseUUIDPipe(入力をUUID型に変換)
- DefaultValuePipe(入力がnull, undefinedの場合にデフォルト値を与える)
変換のパイプも変換ができなかった場合に例外を出すので、バリデーションとしても使えます。
Pipeをアプリケーションに適用させる3つの方法
以下の3つの方法でPipeをアプリケーションに適用させます。
方法1: ハンドラに適用する
ハンドラに@UsePipeデコレータをつけ、その引数に使用したいパイプを記述します。この方法は デコレータを適用したハンドラでのみ、パイプを使用できます。
@Post() @UsePipes(ParseIntPipe) create(@Body() createCatDto: CreateCatDto) { // 省略 }
方法2: パラメータごとに適用する
リクエストパラメータを取得するデコレータの第二引数に使用したいパイプを記述します。パラメータごとにパイプを使えるので、より柔軟に適応することができます。
@Get(':id') findById(@Param('id', ParseUUIDPipe) id: string): Item { return this.itemsService.findById(id); }
方法3: グローバルに適用する
main.tsのapp.useGlobalPipesの引数に使用したいパイプのインスタンスを渡すことで、アプリケーション全体にそのパイプを適用させることができます。
↓ main.ts
import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes(new ValidationPipe()); await app.listen(3000); } bootstrap();
クラスバリデータとは
クラスバリデータとは、入力値バリデーション用のライブラリです。ライブラリであるため、クラスバリデータを使用したい場合、インストールする必要があります。
DTOクラスのプロパティに、クラスバリデータから提供されるデコレータをつけることで、バリデーションのルールを定義します。
クラスバリデータでは、複数のエラーがあると、全て返してくれます。
クラスバリデータを使用してバリデーションを行う
クラスバリデータを使用してバリデーションを行うためには、ValidationPipeを設定する必要があります。全てのエンドポイントに対してバリデーションを有効にしたい場合は、方法3のやり方で ValidationPipe を設定します。
(注)入力チェックには class-validator 、型変換には class-transformerというライブラリを使用します。class-transformerも使う場合、忘れずにインストールするようにしましょう。
NestJSにおける例外処理
NestJSには、組み込みの例外クラスが用意されています。
例)
- BadRequestException
- UnauthorizedException
- NotFoundException
- ConflictException
サービスの中に例外処理を書きます。
以下のコードでは、商品が見つからない場合に例外を返すような処理を書いてます。商品が見つからない場合、フロント側にはステータスコード404のレスポンスが返されます。
findById(id: string): Item { const found = this.items.find((item) => item.id === id); if (found) { return found; } else { throw new NotFoundException(); } }
終わり
NestJSで学んだ箇所はここまでなので、今回が最終回です。今後、NestJSの学習を再開するときは、同じタイトルで記事を書こうと思います。
参考記事
DTOとは - 意味をわかりやすく - IT用語辞典 e-Words