😎

Nest.js Swaggerの@ApiResponseでジェネリクス的なことがしたい

2023/09/12に公開

こういうことがしたい。
(ApiResponseでSwaggerで表示される型を動的に変えたい)
しかしこれだとSwaggerで正しく表示されない。

// 動かないコード
class ResultDataDto<T> {
  @ApiProperty()
  resultData: T;
}

// Controllerで
@Get('/user')
@ApiOkResponse({ type: ResultDataDto<UserResDto> })

ちゃんと表示するには、以下のようにカスタムデコレータを作成して、schemaを定義する必要がある。

import { applyDecorators } from '@nestjs/common';
import { ApiOkResponse, getSchemaPath, ApiExtraModels } from '@nestjs/swagger';

export function ResultDataResponse(model: any) {
  return applyDecorators(
    ApiExtraModels(model),
    ApiOkResponse({
      schema: {
        properties: {
          resultData: { $ref: getSchemaPath(model) }
        },
      },
    }),
  );
}

// Controllerで
@Get('/user')
@ResultDataResponse(UserResDto)

ApiExtraModelsはDTOのimportをよしなにやってくれるメソッドらしい(これがないとCould not resolve reference: #/components/schemas/***となる)。

ページネーションのレスポンス

ページネーションで表示するときに、こんな感じでデコレーターを作っておくと便利。

export class PaginatedResponseDto<T> {
  data: T[]
  @ApiProperty()
  totalCount: number
  @ApiProperty()
  offset: number
  @ApiProperty()
  limit: number
}

// カスタムデコレーター
export const ApiOkResponsePaginated = <DataDto extends Type<unknown>>(dataDto: DataDto) =>
  applyDecorators(
    ApiExtraModels(PaginatedResponseDto, dataDto),
    ApiOkResponse({
      schema: {
        allOf: [
          { $ref: getSchemaPath(PaginatedResponseDto) },
          {
            properties: {
              data: {
                type: 'array',
                items: { $ref: getSchemaPath(dataDto) },
              },
            },
          },
        ],
      },
    })
  )

ページネーションについては、以下の記事で説明されています。
https://aalonso.dev/blog/how-to-generate-generics-dtos-with-nestjsswagger-422g

Discussion