diff --git a/src/app/homepage/pages/recipes/swagger/swagger.component.html b/src/app/homepage/pages/recipes/swagger/swagger.component.html index 6261b6ab80..3bab13847e 100644 --- a/src/app/homepage/pages/recipes/swagger/swagger.component.html +++ b/src/app/homepage/pages/recipes/swagger/swagger.component.html @@ -41,6 +41,37 @@

Bootstrap

The SwaggerModule automatically reflects all of your endpoints. In the background, it's making use of swagger-ui-express and creates a live documentation.

+

Multiple Swagger Specifications

+

+ SwaggerModule also provides a way to support multiple Swagger Specifications. In other words, you can serve different documentations with different SwaggerUI on different endpoints. +

+

+ In order to allow SwaggerModule to support multi-specs, your NestJS application must be written with Modularization approach. + createDocument() method takes in a 3rd argument: extraOptions which is an object where a property include expects an Array of Modules. +

+

+ You can setup Multiple Specifications support as shown below: +

+
{{ secondaryBootstrapFile }}
+

+ Now you can start your server with the following command: +

+

+$ npm run start
+

+ Navigate to http://localhost:3000/api/cats to see SwaggerUI for your Cats: +

+
+

+ While http://localhost:3000/api/docs will expose a SwaggerUI for your Dogs: +

+
+
+ Notice You have to construct a SwaggerOptions with DocumentBuilder, + run createDocument() against newly constructed options then immediately "serve" it with setup() + before you can start working on a second SwaggerOptions for a second Swagger Specification. This specific order is to + prevent Swagger Configurations being overriden by different Options. +

Body, query, path parameters

During the examination of the defined controllers, the SwaggerModule is looking for all used @Body(), @Query(), and @Param() decorators in the route handlers. diff --git a/src/app/homepage/pages/recipes/swagger/swagger.component.ts b/src/app/homepage/pages/recipes/swagger/swagger.component.ts index 8024b6e51c..3f7d343453 100644 --- a/src/app/homepage/pages/recipes/swagger/swagger.component.ts +++ b/src/app/homepage/pages/recipes/swagger/swagger.component.ts @@ -1,14 +1,14 @@ -import { Component, ChangeDetectionStrategy } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { BasePageComponent } from '../../page/page.component'; @Component({ - selector: 'app-swagger', - templateUrl: './swagger.component.html', - changeDetection: ChangeDetectionStrategy.OnPush -}) + selector: 'app-swagger', + templateUrl: './swagger.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + }) export class SwaggerComponent extends BasePageComponent { - get bootstrapFile() { - return ` + get bootstrapFile() { + return ` import { NestFactory } from '@nestjs/core'; import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; import { ApplicationModule } from './app.module'; @@ -28,18 +28,62 @@ async function bootstrap() { await app.listen(3001); } bootstrap();`; - } + } - get postHandler() { - return ` + get secondaryBootstrapFile() { + return ` +import { NestFactory } from '@nestjs/core'; +import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; +import { ApplicationModule } from './app.module'; + +// imports CatsModule and DogsModule; + +async function bootstrap() { + const app = await NestFactory.create(ApplicationModule); + + /** + * createDocument(application, configurationOptions, extraOptions); + * + * createDocument method takes in an optional 3rd argument "extraOptions" + * which is an object with "include" property where you can pass an Array + * of Modules that you want to include in that Swagger Specification + * E.g: CatsModule and DogsModule will have two separate Swagger Specifications which + * will be exposed on two different SwaggerUI with two different endpoints. + */ + + const options = new DocumentBuilder() + .setTitle('Cats example') + .setDescription('The cats API description') + .setVersion('1.0') + .addTag('cats') + .build(); + const catDocument = SwaggerModule.createDocument(app, options, { include: [CatsModule] }); + SwaggerModule.setup('api/cats', app, catDocument); + + const secondOptions = new DocumentBuilder() + .setTitle('Dogs example') + .setDescription('The dogs API description') + .setVersion('1.0') + .addTag('dogs') + .build(); + const dogDocument = SwaggerModule.createDocument(app, secondOptions, { include: [DogsModule] }); + SwaggerModule.setup('api/dogs', app, dogDocument); + + await app.listen(3001); +} +bootstrap();`; + } + + get postHandler() { + return ` @Post() async create(@Body() createCatDto: CreateCatDto) { this.catsService.create(createCatDto); }`; - } + } - get postHandlerJs() { - return ` + get postHandlerJs() { + return ` @Post() @Bind(Body()) @ApiImplicitBody({ name: 'CreateCatDto', type: CreateCatDto }) @@ -47,10 +91,10 @@ async create(createCatDto) { this.catsService.create(createCatDto); } `; - } + } - get createCatDto() { - return ` + get createCatDto() { + return ` import { ApiModelProperty } from '@nestjs/swagger'; export class CreateCatDto { @@ -63,10 +107,10 @@ export class CreateCatDto { @ApiModelProperty() readonly breed: string; }`; - } + } - get createCatDtoJs() { - return ` + get createCatDtoJs() { + return ` import { ApiModelProperty } from '@nestjs/swagger'; export class CreateCatDto { @@ -79,10 +123,10 @@ export class CreateCatDto { @ApiModelProperty({ type: String }) readonly breed; }`; - } + } - get apiModelProperty() { - return ` + get apiModelProperty() { + return ` export declare const ApiModelProperty: (metadata?: { description?: string; required?: boolean; @@ -109,56 +153,56 @@ export declare const ApiModelProperty: (metadata?: { xml?: any; example?: any; }) => PropertyDecorator;`; - } + } - get apiModelPropertyOptional() { - return ` + get apiModelPropertyOptional() { + return ` @ApiModelProperty({ required: false })`; - } + } - get arrayProperty() { - return ` + get arrayProperty() { + return ` @ApiModelProperty({ type: String, isArray: true }) readonly names: string[];`; - } + } - get useTags() { - return ` + get useTags() { + return ` @ApiUseTags('cats') @Controller('cats') export class CatsController {}`; - } + } - get response() { - return ` + get response() { + return ` @Post() @ApiResponse({ status: 201, description: 'The record has been successfully created.'}) @ApiResponse({ status: 403, description: 'Forbidden.'}) async create(@Body() createCatDto: CreateCatDto) { this.catsService.create(createCatDto); }`; - } + } - get customResponse() { - return ` + get customResponse() { + return ` @Post() @ApiCreatedResponse({ description: 'The record has been successfully created.'}) @ApiForbiddenResponse({ description: 'Forbidden.'}) async create(@Body() createCatDto: CreateCatDto) { this.catsService.create(createCatDto); }`; - } + } - get bearerAuth() { - return ` + get bearerAuth() { + return ` @ApiUseTags('cats') @ApiBearerAuth() @Controller('cats') export class CatsController {}`; - } + } - get apiImplicitQuery() { - return ` + get apiImplicitQuery() { + return ` export const ApiImplicitQuery = (metadata: { name: string; description?: string; @@ -168,28 +212,28 @@ export const ApiImplicitQuery = (metadata: { enum?: SwaggerEnumType; collectionFormat?: 'csv' | 'ssv' | 'tsv' | 'pipes' | 'multi'; }): MethodDecorator`; - } + } - get enumImplicitQuery() { - return ` + get enumImplicitQuery() { + return ` @ApiImplicitQuery({ name: 'role', enum: ['Admin', 'Moderator', 'User'] }) async filterByRole(@Query('role') role: UserRole = UserRole.User) { // role returns: UserRole.Admin, UserRole.Moderator OR UserRole.User }`; - } + } - get enumProperty() { - return ` + get enumProperty() { + return ` @ApiModelProperty({ enum: ['Admin', 'Moderator', 'User']}) role: UserRole;`; - } + } - get userRoleEnum() { - return ` + get userRoleEnum() { + return ` export enum UserRole { Admin = 'Admin', Moderator = 'Moderator', User = 'User' }`; - } + } } diff --git a/src/assets/swagger-cats.png b/src/assets/swagger-cats.png new file mode 100644 index 0000000000..68e723415e Binary files /dev/null and b/src/assets/swagger-cats.png differ diff --git a/src/assets/swagger-dogs.png b/src/assets/swagger-dogs.png new file mode 100644 index 0000000000..c5256229cf Binary files /dev/null and b/src/assets/swagger-dogs.png differ