4

I have this route which can return one of these two different DTOs:


  @Get()
  @ApiQuery({ name: 'legacy', description: "'Y' to get houses legacy" })
  async findAllHouses(
    @Query('legacy') legacy: string,
  ): Promise<HousesDto[] | HousesLegacyDto[]> {
  ...
  }

I want to display both of these ResponseDTOs in swagger. I've tried this decorator:

  @ApiOkResponse({
    schema: { oneOf: refs(HousesDto, HousesLegacyDto) },
  })
// OR
  @ApiOkResponse({
    schema: {
      oneOf: [
        { $ref: getSchemaPath(HousesDto) },
        { $ref: getSchemaPath(HousesLegacyDto) },
      ],
    },
  })

with @ApiExtraModels() on top of DTO classes and @ApiProperty() on each properties.

But I still get empty objects in Swagger and I suppose it would not have even taken array types in consideration.

How can I display both of these schemas properly?

swaggerOneOfSchema

Daisy Jess
  • 41
  • 1
  • 3

2 Answers2

9

Seems to me like a lot of very obscure solutions have been posted here and there, so I will try to clarify what needs to be done.

You have two DTOs:

export class SomeStatusDto {
  @ApiProperty({
  description: 'Id',
  example: 1,
  })
  @IsNumber()
  id: number;

  @ApiProperty({
  description: 'Status',
  example: 'in_progress',
  })
  @IsString()
  status: string;
}

export class ErrorStatusDto {
  @ApiProperty({
  description: 'Id',
  example: 1,
  })
  @IsNumber()
  id: number;

  @ApiProperty({
  description: 'Error',
  example: 'Some error string',
  })
  @IsString()
  error: string;
}

Then you have your controller:

  @UseGuards(AccountTypesGuard)
  @ApiOperation({ summary: 'Get status of...' })
  @Get('status')
  @ApiExtraModels(SomeStatusDto, ErrorStatusDto)
  @ApiOkResponse({
    schema: { anyOf: refs(SomeStatusDto, ErrorStatusDto) },
  })
  async getPullStatus(
    @Request() req,
    @Param('id', ParseIntPipe) someId: number,
  ): Promise<SomeStatusDto | ErrorStatusDto> {

    // check if someId belongs to user
    const idBelongsToUser = await this.myService.validateSomeId(
      req.user.id,
      someId,
    );

    if (!idBelongsToUser) {
      throw new ForbiddenException(
        `SomeId does not belong to user (someId=${someId}, userId=${req.user.id})`,
      );
    }

    const key = `status-${someId}`;
    const response = await this.redisService.getByKey(key);
    return response ? response : {};
  }

Note the solution below. You need to reference the DTOs as @ApiExtraModels() and then you can add them as anyOf: refs(...) in your schema.

  @ApiExtraModels(SomeStatusDto, ErrorStatusDto)
  @ApiOkResponse({
    schema: { anyOf: refs(SomeStatusDto, ErrorStatusDto) },
  })

Hope this helps somebody :)

Lior Kupers
  • 528
  • 6
  • 18
2

first schema

second schema

so I encountered a similar issue and this is how you could get the output shown in the image above.

Using the @ApiResponse decorator you could set the two responses using the examples property, try the code sample below

@ApiResponse({
    status: 200,
    description: 'Successful response',
    content: {
      'application/json': {
        examples: {
          HousesDto: { value: HousesDto },
          HousesLegacyDto: { value: HousesLegacyDto },
        },
      },
    },
})