DEV Community

Iulian Preda
Iulian Preda

Posted on

Nx NestJs - How to autogenerate OpenApi/Swagger specs

Hello everyone, short article this time.
As I started using NestJs with Nx I noticed a point of contingency, there are still debates on how to activate OpenApi/Swagger in NestJs with the auto documentation option.

Fear no more, at the end of this tutorial that problem will be solved.

Versions used

I am using the latest Nx at the time of writing, which is V14, with NestJs 8.0 and NestJs Swagger 5.2.

Packages

Install the required package first, if you use express install npm install --save @nestjs/swagger swagger-ui-express otherwise, for fastify use npm install --save @nestjs/swagger fastify-swagger

Add Swagger Module

In main.ts from your NestJs' project folder add the following:

function setupOpenApi(app: INestApplication) {
  const config = new DocumentBuilder().setTitle('API Documentation').setVersion('1.0').addTag('api').build();
  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, document);
}
Enter fullscreen mode Exit fullscreen mode

then in the bootstrap function just call the setupOpenApi function.

My entire main.ts looks like this, take note that I prefered the following options:

  • I used Fastify, because I don't have any hard dependency on an express middleware and it improves the performance
  • I Used a global prefix, thus the I had to set useGlobalPrefix:true in the function above
  • The global prefix I used is the default api that conflicts with the default route for OpenApi which in the documentation is coincidently api. If you keep them like this without adding the useGlobalPrefix: true flag then you will be able to access both the APIs and the OpenAPI on http://localhost:3000/api but you will notice that the documentation is wrong as it doesn't contain the globalPrefix used. To fix this we will have to set the useGlobalPrefix: true flag for OpenAPI and access the documentation at http://localhost:3000/api/api which looks weird so I customized it to http://localhost:3000/api/openApi Working example bellow:
/**
 * This is not a production server yet!
 * This is only a minimal backend to get started.
 */
import { INestApplication, Logger } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';

import { AppModule } from './app/app.module';

async function bootstrap() {
  const fastifyOptions: ConstructorParameters<typeof FastifyAdapter>[0] = {
    logger: true,
  };
  const fastifyAdapter = new FastifyAdapter(fastifyOptions);
  const app = await NestFactory.create<NestFastifyApplication>(AppModule, fastifyAdapter);

  const globalPrefix = 'api';
  app.setGlobalPrefix(globalPrefix);
  setupOpenApi(app);

  const port = process.env.PORT || 3333;
  await app.listen(port);
  Logger.log(`🚀 Application is running on: http://localhost:${port}/${globalPrefix}`);
}

bootstrap();

function setupOpenApi(app: INestApplication) {
  const config = new DocumentBuilder().setTitle('API Documentation').setVersion('1.0').addTag('api').build();
  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('openApi', app, document, { useGlobalPrefix: true });
}

Enter fullscreen mode Exit fullscreen mode

Right now you are ready to use the manual documentation part of the package, but we both know you are not here for that

Auto Documentation

This will be rather short, we will have 2, non-hacky, ways to do this.

Nest-Cli.json - Not suggested

Just create a nest-cli.json file at the root of the project, then add

{
  "collection": "@nestjs/schematics",
  "sourceRoot": "apps",
  "compilerOptions": {
    "plugins": [
      {
        "name": "@nestjs/swagger/plugin",
        "options": {
          "dtoFileNameSuffix": [".entity.ts", ".dto.ts"],
          "controllerFileNameSuffix": [".controller.ts"],
          "classValidatorShim": true,
          "dtoKeyOfComment": "description",
          "controllerKeyOfComment": "description",
          "introspectComments": true
        }
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Do not be scared of the above settings, they are just the defaults to be more explicit. What is really changed is the sourceRoot which in the case on NX (at least for the presets with apps) is the folder apps.

This should detect your parameters, and class names, but will most likely not see what is inside your classes and they will appear empty.

TsPlugin - Recommended, more customizable

Go to your project.json in your NestJs' project folder or angular.json in the root folder for older version and search for the project you need. We will need to alter the build command.

The entire build command is displayed bellow

 "build": {
      "executor": "@nrwl/node:webpack",
      "outputs": ["{options.outputPath}"],
      "options": {
        "outputPath": "dist/apps/api",
        "main": "apps/api/src/main.ts",
        "tsConfig": "apps/api/tsconfig.app.json",
        "tsPlugins": [
          {
            "name": "@nestjs/swagger/plugin",
            "options": {
              "dtoFileNameSuffix": [".entity.ts", ".dto.ts"],
              "controllerFileNameSuffix": [".controller.ts"],
              "classValidatorShim": true,
              "dtoKeyOfComment": "description",
              "controllerKeyOfComment": "description",
              "introspectComments": true
            }
          }
        ],
        "assets": ["apps/api/src/assets"]
      },
Enter fullscreen mode Exit fullscreen mode

As you can notice we added a custom Webpack TsPlugin as specified in the documentation above this https://docs.nestjs.com/openapi/cli-plugin#integration-with-ts-jest-e2e-tests.

As mentioned, this is the recommended approach, it will see all the classes from anywhere in the monorepo as long as they are imported into the project.

Final details

Keep in mind the following:

  • You can use only classes not interfacees
  • You need to append a .dto.ts or .entity.ts unless you change that

Thanks for reading!

Discussion (0)