sroool
sroool

Reputation: 145

generating swagger json file without running nest js server


does anyone know of a way(either official or through a 3rd-party tool) to generate swagger json files without needing the nest js server to run?

I have a nest js application with controller routes and DTO's annotated with @nest/swagger decorators for documentation. I know I can get the swagger json files by starting the server and visiting /api-json but I need the ability to generate this file without having to start the server first.

Upvotes: 9

Views: 2971

Answers (2)

Hiro
Hiro

Reputation: 578

In my case, PostgreSQL datasource was a dependency, so I eventually decided to use memory database, pg-mem to run on e2e test. Here is how I achieved the swagger.json export.

src/app.module.ts

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      load: [generalConfig],
    }),
    TypeOrmModule.forRootAsync({
      name: 'default',
      imports: [ConfigModule, SharedModule],
      useClass: DatabaseConfig,
    }),
    SharedModule,
    InfoModule,
  ],
})
export class AppModule {}

test/swagger.e2e-spec.ts

import request from 'supertest';
import * as path from 'path';
import { writeFileSync } from 'fs';
import { DataType, newDb } from 'pg-mem';
import { DataSource } from 'typeorm';
import { INestApplication } from '@nestjs/common';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { Test } from '@nestjs/testing';
import { AppModule } from '../src/app.module';

describe('SwaggerController (e2e)', () => {
  let app: INestApplication;

  beforeAll(async () => {
    const db = newDb();
    db.public.registerFunction({
      name: 'current_database',
      args: [],
      returns: DataType.text,
      implementation: () => 'localdb',
    });
    db.public.registerFunction({
      name: 'version',
      args: [],
      returns: DataType.text,
      implementation: () => '1',
    });
    // Get PG in memory DB connection
    const datasource = (await db.adapters.createTypeormDataSource({
      type: 'postgres',
      autoLoadEntities: true,
      synchronize: true,
    })) as any;
    await datasource.initialize();
    await datasource.synchronize();

    const moduleFixture = await Test.createTestingModule({
      imports: [AppModule],
    })
      .overrideProvider(DataSource)
      .useValue(datasource)
      .compile();

    app = moduleFixture.createNestApplication();

    const config = new DocumentBuilder()
      .setTitle('NestJS App')
      .setDescription('NestJS App description')
      .setVersion('1.0')
      .build();
    const document = SwaggerModule.createDocument(app, config);
    SwaggerModule.setup('docs', app, document);

    await app.init();
  });

  afterAll(async () => {
    await app.close();
  });

  it('/docs (GET)', () => {
    return request(app.getHttpServer()).get('/docs').expect(200);
  });

  it('/docs-json (GET)', () => {
    return request(app.getHttpServer())
      .get('/docs-json')
      .expect(200)
      .expect((res) => {
        const swaggerJson = JSON.stringify(res.body, null, 2);
        const outputPath = path.resolve(process.cwd(), 'swagger.json');
        writeFileSync(outputPath, swaggerJson, { encoding: 'utf8' });
      });
  });
});

If you rely on "@nestjs/swagger plugin at nest-cli.json such as for schema definition property generation, the config is not applied on e2e execution by default as ts-jest compiles your source code files on the fly, in memory (Ref: https://docs.nestjs.com/openapi/cli-plugin#integration-with-ts-jest-e2e-tests) So you need to create a config file below

test/swagger.transformer.config.js

const transformer = require('@nestjs/swagger/plugin');

module.exports.name = 'nestjs-swagger-transformer';
// you should change the version number anytime you change the configuration below - otherwise, jest will not detect changes
module.exports.version = 1;

module.exports.factory = (cs) => {
  return transformer.before(
    {
      // @nestjs/swagger/plugin options (can be empty)
    },
    cs.program, // "cs.tsCompiler.program" for older versions of Jest (<= v27)
  );
};

and update jest-e2e.json in transform block

{
  ...
  "transform": {
    "^.+\\.(t|j)s$": [
      "ts-jest",
      {
        "astTransformers": {
          "before": ["./test/swagger.transformer.config.js"]
        }
      }
    ]
  }
}

Upvotes: 0

mh377
mh377

Reputation: 1836

I managed to generate a swagger file from my e2e tests without starting the server.

The code below generates a swagger spec in a *.json file that you can paste into https://editor.swagger.io/

// my-api.e2e-spec.ts

import { Test, TestingModule } from '@nestjs/testing';
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
import { HttpModule } from '@nestjs/axios';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import * as fs from 'fs';

describe('My E2E Tests', () => {
  let app: NestFastifyApplication;

  beforeAll(async () => {
    const module: TestingModule = await Test.createTestingModule({
      imports: [HttpModule],
    }).compile();

    app = module.createNestApplication(new FastifyAdapter());
    app.setGlobalPrefix('/api/v1');
    await app.init();
    await app.getHttpAdapter().getInstance().ready();
  });

  afterAll(async () => {
    await app.close();
  });

  it('should generate swagger spec', async () => {
    const config = new DocumentBuilder().setTitle('My API').setDescription('My API').setVersion('1.0').build();

    const document = SwaggerModule.createDocument(app, config);
    fs.writeFileSync('./swagger.json', JSON.stringify(document));
  });
});

Note: My version of @nestjs/swagger in my package.json is 5.2.0

Upvotes: 7

Related Questions