🐕

Nest.jsでQueryの型変換を有効にする

2022/01/24に公開約2,200字

Nest.jsでQueryを使用したい際、Controllerで以下のように設定することがあると思います。

@Get()
async findAll(@Query('name') name: string): Promise<HogeEntity> {
  console.log('typeof', typeof name)
}

この場合のnameの型はもちろんstringです。

本題の問題

では、Queryでnumberやbooleanを取得したい場合はどうでしょうか?

@Get()
async findAll(@Query('enable') enable: boolean): Promise<HogeEntity> {
  console.log('typeof', typeof enable)
}
@Get()
async findAll(@Query('age') age: number): Promise<HogeEntity> {
  console.log('typeof', typeof age)
}

答えは...

typeof enable string
typeof age string

このようにどちらもstring型として定義されてしまいます。
しかし、TypeScript上での方は依然としてbooleannumber型なので、一見してこの矛盾に気づくことはできません。
なので、知らず知らずのうちに本来boolean型で処理しないといけないところをstring型で処理してしまったりしてしまいます。

解決方法

そこで使用するのがPipeです。

https://docs.nestjs.com/pipes

リクエストからControllerで処理されるまでの間でQueryやParam、Bodyなどを検証・変換する層のことをPipeと言います。
ですので、今回でいうとstring型で来ているQueryをboolean型やnumber型に変換するのがこのPipeの役割ということになります。

実際にどのようにするかと言うと

async test(
  @Query('age', ParseIntPipe) age: number,
): Promise<void> {
  console.log('typeof age', typeof age)
}

このように@Query('age')の第二引数にParseIntPipeを渡してあげます。
そうすると、Requestから受け取ったQueryをParseIntPipeを通しnumber型に変換することができました。

boolean型に変換したい場合はParseBooleanPipeなど、型に応じて様々なPipeがNest.jsで既に実装されています。
このようにPipeを使用することでQueryを変換することができました。

もっと簡単な解決方法

実はもっと簡単にこのQueryの型変換を行ってくれる方法があります。
それがapp.useGlobalPipesValidationPipeを渡す方法です。

main.ts
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 略
  app.useGlobalPipes(new ValidationPipe({ transform: true }));
  // 略
}

このようにGlobalPipeとしてValidationPipeを使用することで先程のParseIntPipeなどと似たようなことを行なってくれます。

なので、上で出ていた2つの例の答えが...

@Get()
async findAll(@Query('enable') enable: boolean): Promise<HogeEntity> {
  console.log('typeof', typeof enable)
}
@Get()
async findAll(@Query('age') age: number): Promise<HogeEntity> {
  console.log('typeof', typeof age)
}
typeof enable boolean
typeof age number

とちゃんと期待する通りの型となりました。

最後に

今回のようにNest.jsのQueryでは一見うまく動いてそうな処理でも、実は中で持っている型が違うということが起こり得るので、気をつけましょうという話でした。
自分はこれでTypeORMの検索が中々うまくいかずに詰まっていました...w

Discussion

ログインするとコメントできます