9

I have an app where I define the API response schemas as plain javascript objects according to the open-api spec. Currently I am passing that to the ApiResponse decorator in @nestjs/swagger as follows:

class CatsController {

  @Get()
  @ApiResponse({
    status: 200,
    schema: catSchema // plain js object imported from another file
  })
  getAll() {}
}

This is working great. However, the output open-api spec contains the verbose schema for every endpoint which uses the catSchema. Instead, I want the output swagger file to have the catSchema under the components section, and have a corresponding $ref in the paths section.

components:
  schemas:
    Cat:
      properties:
        name:
          type: string
paths:
  /cats/{id}:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Cat'

So far, it seems the only way to do that would be to define the schema as a DTO class and use the ApiProperty decorator for each class property. In my case, that means I have to refactor all the plain object schemas in open-api spec to be DTO classes.

Is there a way to feed the raw schema to the library and get the expected outcome?

// instead of this:
class CatDto {
  @ApiProperty()
  name: string;
}

// I want to do:
const catSchema = {
  type: 'object',
  properties: {
    name: { type: 'string' }
  }
}
Pubudu Dodangoda
  • 2,742
  • 2
  • 22
  • 38
  • In short, No The reason behind this is: `@ApiProperty` is the tag by which swagger in NestJs understands the schema and it is not supported in inline schema design. Can you explain a bit more in detail on how do you want to pass the schema? – Tanmoy Bhattacharjee Oct 21 '20 at 02:00
  • Thanks for the response @TanmoyBhattacharjee. I want to pass the inline schema to `@ApiResponse` directly. It works well, except it doesn't do `$ref` schemas. The output contains the schema directly under the responses section. Does that make sense? – Pubudu Dodangoda Oct 23 '20 at 13:25

3 Answers3

5

After days and days of trial and error, I was able to pull this off using an interesting trick in Javascript.

First I created the open-api spec as a plain object (as asked in the question). Then passed that to a new decorator, where the magic happens.

In the decorator, I create a DTO class with a predefined name, and map the properties from the plain object to the DTO class. The tricky part is to give it a name dynamically. That can be achieved by the following technique.

const dynamicName = 'foo'; // passed as a parameter to the decorator

class IntermediateDTO {
  @ApiProperty(schema) // schema as a plain object
  data: any;
}

const proxyObject = {
  [dynamicName] = class extends IntermediateDTO {}
}

By using the proxy object, and assigning class extends IntermediateDTO {} to a property in that, the entry gets a name dynamically. Now this new DTO with the dynamic name can be passed to the ApiResponse decorator of @nestjs/swagger to achieve the expected result.

Pubudu Dodangoda
  • 2,742
  • 2
  • 22
  • 38
4

I guess this can also be achieved with using getSchemaPath and ApiExtraModels:

import { ApiExtraModels, ApiResponse, getSchemaPath } from '@nestjs/swagger';

@ApiExtraModels(CatDto) // for CatDto to be found by getSchemaPath()
@ApiResponse({
  schema: {
    '$ref': getSchemaPath(CatDto)
  }
})

More on extra models: https://docs.nestjs.com/openapi/types-and-parameters#extra-models


In my case, that means I have to refactor all the plain object schemas in open-api spec to be DTO classes.

You don't need to manually annotate objects, you can also use this plugin, which is opt-in: https://docs.nestjs.com/openapi/cli-plugin

Anastazy
  • 4,624
  • 2
  • 13
  • 22
  • Thanks for the answer. My question is more about sticking with the plain schema json, rather than converting them to be DTO classes. – Pubudu Dodangoda Apr 26 '22 at 13:57
0

I don't know if this is what you want, I did it like this

@ApiResponse({
    status: 200,
    schema: {
      example: // write the response you want here
      [    
        {
          userId: 1,
          name: 'name',
          
        },
      ],
    },
  })
InnerPeace
  • 35
  • 5
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Mar 09 '22 at 09:35