@nestjs/swaggerが4.x(3.xと並行開発されていたnextブランチ)になり、Open API (Swagger 3.0) がサポートされるようになった。class-validatorを使ったバリデーションと組み合わせたときのデコレータの記述について、一般的なREST APIでの実装例を以下のサンプルプロジェクトにまとめた。
- Controllerクラス実装例:todo.controller.ts
- Resourceクラス実装例:todo.ts
- Parameterクラス実装例:list-query-param.ts
以下では、サンプルプロジェクトを作成する際に気づいたことをまとめている。
クエリパラメータの記述方法
OpenAPI定義に利用する@ApiQuery(@nestjs/swagger)はMethod Decoratorのため、Controllerメソッドに設定する必要がある。一方でバリデーション定義(class-validator)ではProperty DecoratorをDTOクラスに設定する。それぞれ単純に設定すると似たような制約条件の記述場所が離れてしまうので、OpenAPIの制約条件をSchemaObjectに分離して、DTOクラスと同じファイルに記述するとよい。
my-controller.ts
@Controller('path/to/controller')
class MyController {
@ApiQuery({
name: 'myParam',
description: 'A query parameter',
schema: MyParamSchema
})
@Get()
get(@Query() query: MyQuery): MyBody { ... }
}
my-query.ts
export class MyQuery {
@MaxLength(32)
myParam?: string;
}
const MyParamSchema: SchemaObject = {
maxLength: 32,
type: 'string',
}
export { MyParamSchema }
(参考)@ApiPropertyをクエリパラメータに使うときの問題点
単純にDTOクラスにOpenAPIの制約条件を記述する方法として、@ApiPropertyを使うことが考えられるが、このデコレータはリクエストボディに利用されることを想定しているようで、クエリパラメータに設定するとparameters直下に制約条件のプロパティが出力されて、OpenAPIの仕様違反となる。
class MyQuery {
@MaxLength(32)
@ApiProperty({ maxLength: 32 })
param?: string;
}
上記のDTOクラスから出力されるOpenAPIは下記のようになる。
parameters:
- name: myQuery
in: query
maxLength: 100
(OpenAPI仕様上は下記のようにクエリパラメータの制約条件はschemaプロパティに指定する必要がある。)
parameters:
- name: Offset
in: query
schema:
maxLength: 100
クエリパラメータで数値を受け取る
クエリパラメータには型を定義する方法がないため、Nest.jsではstring型として扱われる。数値用のバリデーションデコレータを利用できるようにclass-transformerを使ってnumber型で受け取るとよい。
class QueryParam {
@Type(() => Number)
@Min(0)
@ApiPropertyOptional({
description: 'Offset',
minimum: 0,
example: 5,
})
offset?: number;
}
その他課題など
現時点では以下のような使いにくいポイントがある。(今後改善されていく?)
- @nestjs/commonで定義したリクエストは@nestjs/swaggerに反映されるが、レスポンスは反映されないため、別にOpenAPI用の定義を記述する必要がある。
- 例えば、@HttpCode (common) でレスポンスのステータスコードを変更したとしても、Open APIドキュメントに反映するには別途@ApiResponse (swagger) を記述する必要がある。
- 別のライブラリのため当然だが、class-validatorと@nestjs/swaggerのアノテーションはそれぞれ設定する必要がある。
- ドキュメントに記載する定義域と実際のバリデーションルールを分離したい場合は問題ないが、同じでよい場合は同じルールを2か所に記載することになる。
- Open APIドキュメントを出力するにはひと手間必要になる。(参考:#158)