Nest.jsでQueryの型変換を有効にする
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上での方は依然としてboolean
やnumber
型なので、一見してこの矛盾に気づくことはできません。
なので、知らず知らずのうちに本来boolean
型で処理しないといけないところをstring
型で処理してしまったりしてしまいます。
解決方法
そこで使用するのがPipe
です。
リクエストから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.useGlobalPipes
にValidationPipe
を渡す方法です。
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