Types and parameters

The SwaggerModule searches for all @Body(), @Query(), and @Param() decorators in route handlers to generate the API document. It also creates corresponding model definitions by taking advantage of reflection. Consider the following code:

  1. @Post()
  2. async create(@Body() createCatDto: CreateCatDto) {
  3. this.catsService.create(createCatDto);
  4. }

info Hint To explicitly set the body definition use the @ApiBody() decorator (imported from the @nestjs/swagger package).

Based on the CreateCatDto, the following model definition Swagger UI will be created:

Types and Parameters - 图1

As you can see, the definition is empty although the class has a few declared properties. In order to make the class properties visible to the SwaggerModule, we have to either annotate them with the @ApiProperty() decorator or use the CLI plugin (read more in the Plugin section) which will do it automatically:

  1. import { ApiProperty } from '@nestjs/swagger';
  2. export class CreateCatDto {
  3. @ApiProperty()
  4. name: string;
  5. @ApiProperty()
  6. age: number;
  7. @ApiProperty()
  8. breed: string;
  9. }

info Hint Instead of manually annotating each property, consider using the Swagger plugin (see Plugin section) which will automatically provide this for you.

Let’s open the browser and verify the generated CreateCatDto model:

Types and Parameters - 图2

In addition, the @ApiProperty() decorator allows setting various Schema Object properties:

  1. @ApiProperty({
  2. description: 'The age of a cat',
  3. minimum: 1,
  4. default: 1,
  5. })
  6. age: number;

info Hint Instead of explicitly typing the {{"@ApiProperty({ required: false })"}} you can use the @ApiPropertyOptional() short-hand decorator.

In order to explicitly set the type of the property, use the type key:

  1. @ApiProperty({
  2. type: Number,
  3. })
  4. age: number;

Arrays

When the property is an array, we must manually indicate the array type as shown below:

  1. @ApiProperty({ type: [String] })
  2. names: string[];

info Hint Consider using the Swagger plugin (see Plugin section) which will automatically detect arrays.

Either include the type as the first element of an array (as shown above) or set the isArray property to true.

Circular dependencies

When you have circular dependencies between classes, use a lazy function to provide the SwaggerModule with type information:

  1. @ApiProperty({ type: () => Node })
  2. node: Node;

info Hint Consider using the Swagger plugin (see Plugin section) which will automatically detect circular dependencies.

Generics and interfaces

Since TypeScript does not store metadata about generics or interfaces, when you use them in your DTOs, SwaggerModule may not be able to properly generate model definitions at runtime. For instance, the following code won’t be correctly inspected by the Swagger module:

  1. createBulk(@Body() usersDto: CreateUserDto[])

In order to overcome this limitation, you can set the type explicitly:

  1. @ApiBody({ type: [CreateUserDto] })
  2. createBulk(@Body() usersDto: CreateUserDto[])

Enums

To identify an enum, we must manually set the enum property on the @ApiProperty with an array of values.

  1. @ApiProperty({ enum: ['Admin', 'Moderator', 'User']})
  2. role: UserRole;

Alternatively, define an actual TypeScript enum as follows:

  1. export enum UserRole {
  2. Admin = 'Admin',
  3. Moderator = 'Moderator',
  4. User = 'User',
  5. }

You can then use the enum directly with the @Query() parameter decorator in combination with the @ApiQuery() decorator.

  1. @ApiQuery({ name: 'role', enum: UserRole })
  2. async filterByRole(@Query('role') role: UserRole = UserRole.User) {}

Types and Parameters - 图3

With isArray set to true, the enum can be selected as a multi-select:

Types and Parameters - 图4

Enums schema

By default, the enum property will add a raw definition of Enum on the parameter.

  1. - breed:
  2. type: 'string'
  3. enum:
  4. - Persian
  5. - Tabby
  6. - Siamese

The above specification works fine for most cases. However, if you are utilizing a tool that takes the specification as input and generates client-side code, you might run into a problem with the generated code containing duplicated enums. Consider the following code snippet:

  1. // generated client-side code
  2. export class CatDetail {
  3. breed: CatDetailEnum;
  4. }
  5. export class CatInformation {
  6. breed: CatInformationEnum;
  7. }
  8. export enum CatDetailEnum {
  9. Persian = 'Persian',
  10. Tabby = 'Tabby',
  11. Siamese = 'Siamese',
  12. }
  13. export enum CatInformationEnum {
  14. Persian = 'Persian',
  15. Tabby = 'Tabby',
  16. Siamese = 'Siamese',
  17. }

info Hint The above snippet is generated using a tool called NSwag.

You can see that now you have two enums that are exactly the same. To address this issue, you can pass an enumName along with the enum property in your decorator.

  1. export class CatDetail {
  2. @ApiProperty({ enum: CatBreed, enumName: 'CatBreed' })
  3. breed: CatBreed;
  4. }

The enumName property enables @nestjs/swagger to turn CatBreed into its own schema which in turns makes CatBreed enum reusable. The specification will look like the following:

  1. CatDetail:
  2. type: 'object'
  3. properties:
  4. ...
  5. - breed:
  6. schema:
  7. $ref: '#/components/schemas/CatBreed'
  8. CatBreed:
  9. type: string
  10. enum:
  11. - Persian
  12. - Tabby
  13. - Siamese

info Hint Any decorator that takes enum as a property will also take enumName.

Raw definitions

In some specific scenarios (e.g., deeply nested arrays, matrices), you may want to describe your type by hand.

  1. @ApiProperty({
  2. type: 'array',
  3. items: {
  4. type: 'array',
  5. items: {
  6. type: 'number',
  7. },
  8. },
  9. })
  10. coords: number[][];

Likewise, in order to define your input/output content manually in controller classes, use the schema property:

  1. @ApiBody({
  2. schema: {
  3. type: 'array',
  4. items: {
  5. type: 'array',
  6. items: {
  7. type: 'number',
  8. },
  9. },
  10. },
  11. })
  12. async create(@Body() coords: number[][]) {}

Extra models

To define additional models that are not directly referenced in your controllers but should be inspected by the Swagger module, use the @ApiExtraModels() decorator:

  1. @ApiExtraModels(ExtraModel)
  2. export class CreateCatDto {}

info Hint You only need to use @ApiExtraModels() once for a specific model class.

Alternatively, you can pass an options object with the extraModels property specified to the SwaggerModule#createDocument() method, as follows:

  1. const document = SwaggerModule.createDocument(app, options, {
  2. extraModels: [ExtraModel],
  3. });

To get a reference ($ref) to your model, use the getSchemaPath(ExtraModel) function:

  1. 'application/vnd.api+json': {
  2. schema: { $ref: getSchemaPath(ExtraModel) },
  3. },

oneOf, anyOf, allOf

To combine schemas, you can use the oneOf, anyOf or allOf keywords (read more).

  1. @ApiProperty({
  2. oneOf: [
  3. { $ref: getSchemaPath(Cat) },
  4. { $ref: getSchemaPath(Dog) },
  5. ],
  6. })
  7. pet: Cat | Dog;

If you want to define a polymorphic array (i.e., an array whose members span multiple schemas), you should use a raw definition (see above) to define your type by hand.

  1. type Pet = Cat | Dog;
  2. @ApiProperty({
  3. type: 'array',
  4. items: {
  5. oneOf: [
  6. { $ref: getSchemaPath(Cat) },
  7. { $ref: getSchemaPath(Dog) },
  8. ],
  9. },
  10. })
  11. pets: Pet[];

info Hint The getSchemaPath() function is imported from @nestjs/swagger.

Both Cat and Dog must be defined as extra models using the @ApiExtraModels() decorator (at the class-level).