NestJSにおけるパイプに関するお勉強記録です。主に以下のページを見て学んだ内容になります。
Pipes | NestJS - A progressive Node.js framework
NestJSのパイプとはLaravelでいうFormRequestでバリデーションするような機能です。バリデーションやリクエストデータの型変換などを実施するために使われます。
パイプ
パイプの機能を試すために適当なコードを追加し下準備をします。
下準備
前回までのcats.controller.tsのコードにfindOne()というメソッドを追加します。http://localhost:8001/cats/1とアクセスするとサービスクラスのfindOne()メソッドを呼び出してreturnするだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import { Controller, Get, Post, Param, Body, UseFilters, HttpStatus, HttpException} from '@nestjs/common'; import { CreateCatDto } from './dto/create-cat.dto'; import { CatsService } from './cats.service'; import { Cat } from './interfaces/cat.interface'; import { HttpExceptionFilter } from 'src/common/filter/http-exception.filter'; @Controller('cats') export class CatsController { constructor(private catsService: CatsService) { } @Post() @UseFilters(new HttpExceptionFilter()) async create(@Body() createCatDto: CreateCatDto) { this.catsService.create(createCatDto); } @Get() async findAll(): Promise<Cat[]> { throw new HttpException("エラーメッセージ", HttpStatus.FORBIDDEN); } @Get(':id') async findOne(@Param('id') id: number) { return this.catsService.findOne(id); } } |
cats.service.tsにもメソッドを用意します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
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; } findOne(id: number): Cat { return this.cats[id]; } } |
この状態でhttp://localhost:8001/cats/abcの様に本来数字を期待している所へ文字列を渡しても値が返らないだけでエラーになりません。
パイプの利用
パイプを利用するには1行目にあるように@nestjs/commonからParseIntPipeをインポートし、23行目の様に@Paramデコレータの第2引数に渡してあげるだけです。@Paramだけでなく@Queryや@Bodyでも使えます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import { Controller, Get, Post, Param, Body, UseFilters, HttpStatus, HttpException, ParseIntPipe } from '@nestjs/common'; import { CreateCatDto } from './dto/create-cat.dto'; import { CatsService } from './cats.service'; import { Cat } from './interfaces/cat.interface'; import { HttpExceptionFilter } from 'src/common/filter/http-exception.filter'; @Controller('cats') export class CatsController { constructor(private catsService: CatsService) { } @Post() @UseFilters(new HttpExceptionFilter()) async create(@Body() createCatDto: CreateCatDto) { this.catsService.create(createCatDto); } @Get() async findAll(): Promise<Cat[]> { throw new HttpException("エラーメッセージ", HttpStatus.FORBIDDEN); } @Get(':id') async findOne(@Param('id', ParseIntPipe) id: number) { return this.catsService.findOne(id); } } |
これだけで以下の様なエラーを返すようになります。また、Parseと名が付いているように型の変換もしてくれています。上記の例だと文字列から数値に変換してくれてますのでidは数値として扱えます。
その他のパイプ
以下の様なパイプが用意されているそうです。
ValidationPipe
ParseIntPipe
ParseBoolPipe
ParseArrayPipe
ParseUUIDPipe
DefaultValuePipe
カスタムパイプ
自前のパイプを作るには以下の様なコマンドを実行します。
nest g pipe common/pipe/sample
適用するには同様にインポートして@Paramに渡すだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import { Controller, Get, Post, Param, Body, UseFilters, HttpStatus, HttpException } from '@nestjs/common'; import { CreateCatDto } from './dto/create-cat.dto'; import { CatsService } from './cats.service'; import { Cat } from './interfaces/cat.interface'; import { HttpExceptionFilter } from 'src/common/filter/http-exception.filter'; import { SamplePipe } from 'src/common/pipe/sample.pipe'; @Controller('cats') export class CatsController { constructor(private catsService: CatsService) { } @Post() @UseFilters(new HttpExceptionFilter()) async create(@Body() createCatDto: CreateCatDto) { this.catsService.create(createCatDto); } @Get() async findAll(): Promise<Cat[]> { throw new HttpException("エラーメッセージ", HttpStatus.FORBIDDEN); } @Get(':id') async findOne(@Param('id', SamplePipe) id: number) { return this.catsService.findOne(id); } } |
パイプを生成した直後のコードは以下の様な状態です。valueには検証対象の値が、metadataには名前の通りメタデータが入っています。どんなメタデータが入っているかは見た方が早いと思いますので、以下に上記を実行した場合のサンプルを掲載します。
ここに好きな検証なり変換なりを実施しreturnしてあげます。
1 2 3 4 5 6 7 8 |
import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common'; @Injectable() export class SamplePipe implements PipeTransform { transform(value: any, metadata: ArgumentMetadata) { return value; } } |
元のドキュメントにはjoiとかclass-validator、class-transformerといったライブラリを活用した、もっと高度なやり方が書かれていましたが、長いし説明できるほど理解できないので割愛します。一応今回のサンプルコードを置いておきます。