6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[Nest.js] クエリパラメータをPipeを使って変換する方法

Last updated at Posted at 2021-12-07

この記事は 株式会社サイバー・バズ Advent calendar 2021 8日目の記事です。

概要

Nest.jsを使っている時に、frontから受け取ったRequestを使ってゴネゴネしている時です。

すると、、

「あれ、、、落ちる。。」

「想定した型と合っていない。。。」

ということがありました。

その時の話です。

検索処理を例にあげます。

dto/searchItem.dto.ts
class SearchItemDTO {
  minPrice: number
  maxPrice: number
  page: number
  limit: number
}
item/item.controller.ts
@Get()
  async getItems(@Query() searchItemDTO: SearchItemDTO){
    // 検索処理Service
}

以下のRequestgetItemsに投げました。

curl 'http://localhost:3001/items?minPrice=3000&maxPrice=10000&page=1&limit=10'

ここでgetItemsに実際に渡ってきたsearchItemDTOを確認してみると、
全てstringになっており、SearchItemDTOの型と乖離していることが分かります。

{ minPrice: '3000', maxPrice: '10000', page: '1', limit: '10' }

minPricemaxPriceを使ってnumber前提で計算処理などを書いてしまうと、落ちる危険があります。

Pipes

Nestには、Pipesというものが存在します。

PipesはクライアントからのRequestControllerに渡す前に処理を挟むことができます。

Documentでも取り上げられているように、変換バリデーションの二つが主なユースケースのようです。

Built-in pipes

汎用的なPipesについては、あらかじめBuilt-inで@nestjs/commonからimportして使うことができます。

今回はParseIntPipeが使えそうです。

ParseIntPipeを使ってgetItemsを書き換えてみます。

item/item.controller.ts
  @Get()
  async getItems(
    @Query('minPrice', ParseIntPipe) minPrice: number,
    @Query('maxPrice', ParseIntPipe) maxPrice: number,
    @Query('page', ParseIntPipe) page: number,
    @Query('limit', ParseIntPipe) limit: number
  ){
    // 検索処理Service
  }

再度getItemsに渡ってきたプロパティを確認してみると、

{ minPrice: 3000, maxPrice: 10000, page: 1, limit: 10 }

想定通りnumberに変換されています。

これで目的は果たせましたが、パラメータが増えるたびにParseIntPipeを増やす必要があり、
item.controller.tsファイルが肥大化してしまうおそれがあります。

ユーザ定義pipe

pipe自作することもできます。

まずDTOのうち

変換前をSearchItemDTO

変換後SearchItemTransformDTO

として実際の型と乖離がないように再定義してみます。

dto/searchItem.dto.ts
export class SearchItemDTO {
  minPrice: string
  maxPrice: string
  page: string
  limit: string
}
dto/searchItemTransform.dto.ts
export interface SearchItemTransformDTO {
  minPrice: number
  maxPrice: number
  page: number
  limit: number
}

次にpipe処理です。

pipe/searchItem.pipe.ts
import { PipeTransform, Injectable } from '@nestjs/common'

@Injectable()
export class SearchItemPipe implements PipeTransform<SearchItemDTO, SearchItemTransformDTO> {
  transform(searchItemDTO: SearchItemDTO): SearchItemTransformDTO {
    return Object.entries(searchItemDTO).reduce((acc, cur) => {
      const key = cur[0]
      const value = ['minPrice', 'maxPrice', 'page', 'limit'].includes(key) ? parseInt(cur[1]) : cur[1]
      return { ...acc, [key]: value }
    }, {})
  }
}

SearchItemPipeitem.controller.tsに反映させます。

item/item.controller.ts
@Get()
  async getItems(@Query(SearchItemPipe) searchItemTransformDTO: SearchItemTransformDTO){
    // 検索処理Service
}

すっきり書けました。

パラメータが増えた場合は、SearchItemPipeを編集すれば対応できます。

これで定義したDTOと実際の値との間で乖離が無くなりました

他にも方法はあるかと思いますが、一例として参考にしていただければと思います。

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?