Reputation: 145
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
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
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