14

I am working on a node.js server that uses the NestJS framework. I want to auto-build API documentation for the app using NestJS's swagger integration.

Documentation generated correctly for my controller methods that the utilized @Body() approach for controller data exchange. It did not work correctly for controller methods that used the @Param() approach. An example controller that fails to generate correct documentation:

  @Get('/:identifier')
  @RouteLogger()
  @ApiParam({name: 'identifier', required: true, description: 'either an integer for the project id or a string for the project name', schema: { oneOf: [{type: 'string'}, {type: 'integer'}]}})
  async getProject(
    @Param('identifier')
    identifier: string | number,
    @Res() res: Response
  ) { }

This generates the following in the swagger UI:

enter image description here

You can see that the endpoint in swagger UI fails to show the endpoint having any parameters. What is the correct way of writing a GET endpoint for a nestJS controller with @Params such that swagger will correctly generate documentation?

GrantD71
  • 1,787
  • 3
  • 19
  • 27

4 Answers4

12

It appears as though my custom decorator @RouteLogger() was in some way conflicting with the swagger doc generation.

Documentation generated correctly once I moved that decorator below the API @ApiParam() decorator:

  @Get('/:identifier'
  @ApiParam({name: 'identifier', required: true, description: 'either an integer for the project id or a string for the project name', schema: { oneOf: [{type: 'string'}, {type: 'integer'}]}})
  @RouteLogger()
  async getProject(
    @Param('identifier')
    identifier: string | number,
    @Res() res: Response
  ) { }
GrantD71
  • 1,787
  • 3
  • 19
  • 27
5

Glad you already found a solution!

You can also use OpenAPI's CLI Plugin to get these parameters automatically (without use decorators), as mentioned on docs: https://docs.nestjs.com/openapi/cli-plugin.

To do that, you only need to change nest-cli.json, including compilerOptions, like that:

{
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "plugins": ["@nestjs/swagger"]
  }
}

Or like that, if you need to pass options to plugin:

{
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "plugins": [
      {
        "name": "@nestjs/swagger/plugin",
        "options": {
          "dtoFileNameSuffix": [
            ".entity.ts",
            ".dto.ts"
          ],
          "controllerFileNameSuffix": [
            ".controller.ts"
          ]
        }
      }
    ]
  }
}
2
 @Get('/:identifier')
  @RouteLogger()
  @ApiParam({
    name: 'identifier',
    required: true,
    description: 'either an integer for the project id or a string for the project name', 
    schema: { oneOf: [{type: 'string'}, {type: 'integer'}]},
    type: 'string' | 'integer'
  })
  async getProject(
    @Param('identifier')
    identifier: string | number,
    @Res() res: Response
  ) { }
Umar
  • 49
  • 2
  • While this *may* be a valid solution, your answer could be improved by adding an explanation of how it solves the problem. Don't forget to quote and link to any relevant documentation. – Besworks Jun 03 '22 at 16:22
0
function setSwaggerPaths(document: OpenAPIObject): OpenAPIObject {
  for (const path of Object.keys(document.paths)) {
    let is_open = false;
    const params: [{ name: string, in: 'query' | 'header' | 'path' | 'cookie' }] = [{ name: 'any', in: 'cookie' }];
    let param = '';
    params.shift()
    for (let i = 0; i < path.length; i++) {
      if (path[i] === '{')
        is_open = true;
      if (is_open && path[i] !== '}' && path[i] !== '{')
        param += path[i];

      if (is_open && param.length && path[i + 1] === '}') {
        params.push({ name: param, in: 'path' })
      }
      if (path[i + 1] === '}') {
        is_open = false;
        param = ''
      }
    }
    if (path.includes('{')) {
      for (const method of Object.keys(document.paths[path])) {
        document.paths[path][method].parameters?.unshift(...params)
      }
    }
  }
  return document;
}

async function bootstrap() {
  const app: NestExpressApplication = await NestFactory.create(AppModule, {
    logger: ['error', 'warn'],
    cors: {}
  });
  document = SwaggerModule.createDocument(app, swagger_config);

  // set swagger api params
  document = setSwaggerPaths(document);
  SwaggerModule.setup("docs", app, document, swagger_options);
  await app.listen(3000);
}
bootstrap();
Umar
  • 49
  • 2
  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/31884847) – MD. RAKIB HASAN Jun 01 '22 at 08:49