タイトル通り、NestJS で開発を進める中で生じた「Do not know how to serialize a BigInt」というエラーが生じた際の対処法についてメモ書き。
原因
[Nest] 4865 - 2023/03/19 19:06:06 ERROR [ExceptionsHandler] Do not know how to serialize a BigInt
TypeError: Do not know how to serialize a BigInt
直訳すると、「BigInt をシリアライズする方法を知りません」といった感じでしょうか...。
そもそもの仕様として、JSON においては BigInt のシリアライズに対応していないようです。
対処法
これに対するアプローチは色々と存在するようですが、NestJS でどう対処すれば良いのか調べてみました。
状況によっても異なるとは思いますが、今回は下記のようなケースを想定としています。
- ユーザー一覧の情報を JSON 形式で取得できる
/api/v1/users
というエンドポイント - ユーザーの情報には BigInt 型のデータが含まれる
たとえば、こんな感じのコードがあるとして...
import { Controller, Get } from '@nestjs/common';
import { IsString, IsInt, IsArray } from 'class-validator';
class UserDto {
@IsInt()
id: number;
@IsInt()
bigIntId: bigint;
@IsString()
name: string;
constructor(partial: Partial<UserDto>) {
Object.assign(this, partial)
}
}
export class FindAllUserResponseDto {
@IsArray()
users: UserDto[]
constructor(partial: Partial<FindAllUserResponseDto>) {
Object.assign(this, partial)
}
}
@Controller({ path: 'users', version: '1' })
export class UsersController {
@Get('/users')
async findAll() {
const users = [
{ id: 1, bigIntId: BigInt(12345678901234), name: '山田 太郎' },
{ id: 2, bigIntId: BigInt(56789012345678), name: '鈴木 一郎' },
];
return new FindAllUserResponseDto({
users: users.map((user) => {
return new UserDto({
id: user.id,
bigIntId: user.bigIntId,
name: user.name,
})
})
})
}
}
このままだと先述のエラーが発生してしまいました。
エラーを解消するためには、以下のように修正します。
import { Controller, Get, UseInterceptors, ClassSerializerInterceptor } from '@nestjs/common';
import { IsString, IsInt, IsArray } from 'class-validator';
import { Transform } from 'class-transformer';
class UserDto {
@IsInt()
id: number;
@IsInt()
@Transform(({ value }) => value.toString()) // BigIntを文字列に変換する
bigIntId: bigint;
@IsString()
name: string;
constructor(partial: Partial<UserDto>) {
Object.assign(this, partial)
}
}
export class FindAllUserResponseDto {
@IsArray()
users: UserDto[]
constructor(partial: Partial<FindAllUserResponseDto>) {
Object.assign(this, partial)
}
}
@Controller({ path: 'sites', version: '1' })
export class SitesController {
@Get('/sites')
@UseInterceptors(ClassSerializerInterceptor) // ClassSerializerInterceptor を使用する
async findAll() {
const users = [
{ id: 1, bigIntId: BigInt(12345678901234), name: '山田 太郎' },
{ id: 2, bigIntId: BigInt(56789012345678), name: '鈴木 一郎' },
];
return new FindAllUserResponseDto({
users: users.map((user) => {
return new UserDto({
id: user.id,
bigIntId: user.bigIntId,
name: user.name,
})
})
})
}
}
- UserDto で @Transform デコレーターを使用して、BigInt の値を文字列に変換
- ClassSerializerInterceptor を使用して、UserDto をシリアル化
このように、NestJS では ClassSerializerInterceptor と @Transform デコレーターの組み合わせで BigInt をシリアライズすることができました。
[
{
"id": "1",
"bigIntId": "12345678901234",
"name": "山田 太郎"
},
{
"id": 2,
"bigIntId": "56789012345678",
"name": "鈴木 一郎"
}
]