パイプは、@Injectable()
デコレーターでアノテーションされていてかつPipeTransform
インターフェースを実装しているクラスのことである。
用途は、コントローラーのルートハンドラで、@Query()
や@Param()
などで受け取った入力データを希望の型やフォーマットに変換するために使われる。
あとは、その受け取った入力データがこのメソッドの入力データとして正しいかどうかなどを検証するために使われる。正しくない場合は例外をスローする。
個人的に便利なのが、ルートメソッドで受け取る入力データは基本的に文字列なので、それをDate
型などに変換したりすることで、ルートメソッドで変換処理を実装しなくて済むようになるところ。
実際に作ってみる。パイプ名は-
で単語間を結ぶようにする。語尾に*-pipe
をつけなくて良い。自動的に付与される。
作成されるファイル名は*.pipe.ts
になる。
nest g pipe parse-date
srcディレクトリに、parse-date.pipe.tsとparse-date.pipe.spec.tsファイルが作成される。
transform
メソッドに処理内容を実装していく。
ParseTransform
の第一型引数には受け取る入力データの型を指定する。
第二型引数にはtransform
メソッドの返り値の型を指定する。
transform
メソッドの引数のvalue
はデフォルトでany
型なので、具体的な型を指定しておくのもいいと思う。返り値の型も同様。
// src/pipes/parse-date.pipe.ts import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common'; @Injectable() export class ParseDatePipe implements PipeTransform<string | undefined, Date | undefined> { transform( value: string | undefined, metadata: ArgumentMetadata, ): Date | undefined { if (!value) { return undefined; } if (typeof value !== 'string') { return undefined; } try { const time = parseInt(value, 10); return new Date(time); } catch (e: unknown) { if (e instanceof Error) { console.log(`e: ${e.message}`); } return undefined; } } }
テストも一緒に書いておく。
// src/pipes/parse-date.pipe.spec.ts import { ParseDatePipe } from './parse-date.pipe'; describe('ParseDatePipe', () => { it('should be defined', () => { expect(new ParseDatePipe()).toBeDefined(); }); it('ミリ秒を表す文字列を渡すとDate型に変換する', () => { const pipe = new ParseDatePipe(); const baseDate = new Date().getTime(); const result = pipe.transform(`${baseDate}`, {} as any); expect(result).toBeDefined(); expect(result.getTime()).toBe(baseDate); }); it('undefinedを渡すとエラーの代わりにundefinedを返す', () => { const pipe = new ParseDatePipe(); const result = pipe.transform(undefined, {} as any); expect(result).toBeUndefined(); }); });
使うときはルートメソッドの引数に指定したデコレーターの第二引数にクラスをそのまま渡すだけ。
@Get() async findAll( @Req() request, @Query('base_date', ParseDatePipe) baseDate: Date | undefined ) {} ...