Aller au contenu principal
Version: 3.0.0

Swagger

Based on the latest OpenAPI 3.0.3, the new version of Swagger components is implemented.

Related information:

Description
Can be used for standard projects
Can be used for Serverless
Can be used for integration
Contains independent main framework
Contains independent logs

Installation dependency

$ npm install @midwayjs/swagger@3 --save
$ npm install swagger-ui-dist --save-dev

If you want to output Swagger API pages on the server, you need to install the swagger-ui-dist into the dependency.

$ npm install swagger-ui-dist --save

Or reinstall the following dependencies in package.json.

{
"dependencies": {
"@midwayjs/swagger": "^3.0.0",
// If you want to use it on the server
"swagger-ui-dist": "4.2.1",
// ...
},
"devDependencies": {
// If you don't want to use it on the server
"swagger-ui-dist": "4.2.1",
// ...
}
}

Open the component

Add components to configuration.ts.

import { Configuration } from '@midwayjs/core';
import * as swagger from '@midwayjs/swagger';

@Configuration({
imports: [
// ...
swagger
]
})
export class MainConfiguration {

}

You can configure the enabled environment, for example, the following code refers to "only enabled in local environment".

import { Configuration } from '@midwayjs/core';
import * as swagger from '@midwayjs/swagger';

@Configuration({
imports: [
// ...
{
component: swagger
enabledEnvironment: ['local']
}
]
})
export class MainConfiguration {

}

Then start the project and access the address:

The path can be configured by swaggerPath parameters.

Data type

Automatic type extraction

The Swagger component identifies the @Body(), @Query(), @Param() decorators for each routing method in each @Controller and extracts routing method parameters and types.

For example, the following code:

@Get('/')
async home (
@Query('uid') uid: number
@Query('tid') tid: string
@Query('isBoolean') isBoolean: boolean
) {
// ...
}

The basic Boolean, string, and numeric types are displayed as follows:

Types and schema

We often use objects in parameters and use defined classes as types. At this time, swagger components can also be automatically identified, and can also be combined with common types for identification.

For example, the following code:

@Post('/:id', { summary: 'test'})
async create(@Body() createCatDto: CreateCatDto, @Param('id') id: number) {
// ...
}

The definition of CreateCatDto type is as follows. We use ApiProperty to define each attribute.

import { ApiProperty } from "@midwayjs/swagger";

export class CreateCatDto {
@ApiProperty({ example: 'Kitty', description: 'The name of the Catname'})
name: string;

@ApiProperty({ example: '1', description: 'The name of the Catage'})
age: number;

@ApiProperty({ example: 'bbbb', description: 'The name of the Catbreed'})
breed: string;
}

The effect is as follows. The component will automatically extract two of the parameters:

swagger1

At the same time, since the example of each attribute is defined in the class, the sample value is automatically filled in.

In Swagger, each type will be described by a Schema. We have already defined a CreateCatDto Schema, which looks like the following.

Note that we will reuse these Schemas.

swagger2

Base type

We can define common types by setting type in the @ApiProperty(...) decorator.

In most cases, the underlying type can be automatically identified without explicitly declaring the type.

String

@ApiProperty({
type: 'string',
// ...
})
name: string;

Boolean

@ApiProperty({
type: 'boolean',
example: 'true',
// ...
})
isPure: boolean;

Number Type

@ApiProperty({
type: 'number',
example: '1',
description: 'The name of the Catage'
})
age: number;

In addition, you can also use the format field to define a more precise length.

@ApiProperty({
type: 'integer',
format: 'int32',
example: '1',
description: 'The name of the Catage'
})
age: number;

Array type

If the array type is an array type, you can configure the type field and use the type of the items to specify the type.

@ApiProperty({
type: 'array',
items: {
type: 'string',
},
example: ['1']
}
})
breeds: string[];

Enumeration type

If it is an enumeration type, it can be defined by configuring the enmu field.

enum HelloWorld {
One = 'One',
Two = 'Two',
Three = 'Three',
}

@ApiProperty({
enum: ['One', 'Two', 'Three']
description: 'The name of the Catage'
})
hello: HelloWorld;

If the field is at the top level, the display effect is as follows:

swagger3

Complex object types

If the type of a property is an existing complex type, you can use the type parameter to specify the complex type.

export class Cat {
/**
* The name of the Catcomment
* @example Kitty
*/
@ApiProperty({ example: 'Kitty', description: 'The name of the Cat'})
name: string;

@ApiProperty({ example: 1, description: 'The age of the Cat' })
age: number;

@ApiProperty({ example: '2022-12-12 11:11:11', description: 'The age of the CatDSate' })
agedata?: Date;

@ApiProperty({
example: 'Maine Coon',
description: 'The breed of the Cat',
})
breed: string;
}

export class CreateCatDto {

// ...

@ApiProperty({
Type: Cat, // There is no need to specify example here.
})
related: Cat;
}

The effect is as follows:

Complex object array type

If the type of an attribute is a complex array type, the writing is slightly different.

Except that the type is declared as array, the items property only supports strings. You must use the getSchemaPath method to import a different type.

In addition, if the Cat type has not been declared in the type field of other attributes, you need to use the @ApiExtraModel decorator to declare additional external types.

import { ApiProperty, getSchemaPath, ApiExtraModel } from '@midwayjs/swagger';

class Cat {
// ...
}

@ApiExtraModel(Cat)
export class CreateCatDto {
// ...

@ApiProperty({
type: 'array',
items: {
$ref: getSchemaPath(Cat)
}
})
relatedList: Cat[];
}


The effect is as follows:

Circular dependencies

When you have circular dependencies between classes, use lazy functions to provide type information.

For example looping over the type field.

class Photo {
// ...
@ApiProperty({
type: () => Album
})
album: Album;
}
class Album {
// ...
@ApiProperty({
type: () => Photo
})
photo: Photo;
}

getSchemaPath can also be used.

export class CreateCatDto {
// ...

@ApiProperty({
type: 'array',
items: {
$ref: () => getSchemaPath(Cat)
}
})
relatedList: Cat[];
}

Request definition

The paths defined by the OpenAPI are each routing path, and each routing path has the definition of HTTP methods, such as GET, POST, DELETE, PUT, etc.

Query definition

Use @ApiQuery to define Query data.

The @Query decorator is automatically identified.

@Get('/get_user')
async getUser(@Query('name') name: string) {
return 'hello';
}

If @Query is in the form of an object, you need to specify a name parameter in @ApiQuery, and the object type needs to be used with @ApiProperty, otherwise the form will become read-only.

export class UserDTO {
@ApiProperty()
name: string;
}

@Get('/get_user')
@ApiQuery({
name: 'query'
})
async getUser(@Query() dto: UserDTO) {
// ...
}

Body definition

Use @ApiBody to define Body data.

The @Body object type needs to be used with @ApiProperty.

export class UserDTO {
@ApiProperty()
name: string;
}

@Post('/update_user')
async upateUser(@Body() dto: UserDTO) {
// ...
}

For additional details, please use @ApiBody enhancement.

File upload definition

Set contentType with @ApiBody

@Post('/:id', { summary: 'test'})
@ApiBody({
description: 'this is body',
contentType: BodyContentType.Multipart
})
@ApiParam({ description: 'this is id' })
async create(@Body() createCatDto: CreateCatDto, @Param('id') id: number): Promise<Cat> {
return this.catsService.create(createCatDto);
}

Use @ApiProperty to add format in CreateCatDto

@ApiProperty({
type: 'string',
format: 'binary',
description: 'this is file test'
})
file: any;

The Swagger UI shows: swagger4

attention

If you want to display the upload information swagger, please add the type (the type corresponding to the decorator and the type in the @ApiBody), otherwise an error will be reported.

Compatible with Upload components, add @ApiBody() decorator description

@Post('/test')
@ApiBody({ description: 'hello file' })
@ApiBody({ description: 'hello fields', type: Cat })
async upload(@File() f: any, @Fields() data: Cat) {
// ...
}

The Swagger UI shows: swagger5

Do not add @ApiBody() decorator description

@Post('/test1')
async upload1(@Files() f: any[], @Fields() data: Cat) {
// ...
}

The Swagger UI shows: swagger6

Request Header

The Header parameter is defined by the @ApiHeader({...}) decorator.

@ApiHeader({
name: 'x-test-one',
description: 'this is test one'
})
@ApiTags(['hello'])
@Controller('/hello')
export class HelloController {}

Request Response

@ApiResponse({...}) can be used to customize request Response.

@Get('/:id')
@ApiResponse({
status: 200
description: 'The found record',
type: Cat
})
findOne(@Param('id') id: string, @Query('test') test: any): Cat {
return this.catsService.findOne(+id);
}

Other decorators that do not require status are also available:

  • @ApiOkResponse()
  • @ApiCreatedResponse()
  • @ApiAcceptedResponse()
  • @ApiNoContentResponse()
  • @ApiMovedPermanentlyResponse()
  • @ApiBadRequestResponse()
  • @ApiUnauthorizedResponse()
  • @ApiNotFoundResponse()
  • @ApiForbiddenResponse()
  • @ApiMethodNotAllowedResponse()
  • @ApiNotAcceptableResponse()
  • @ApiRequestTimeoutResponse()
  • @ApiConflictResponse()
  • @ApiTooManyRequestsResponse()
  • @ApiGoneResponse()
  • @ApiPayloadTooLargeResponse()
  • @ApiUnsupportedMediaTypeResponse()
  • @ApiUnprocessableEntityResponse()
  • @ApiInternalServerErrorResponse()
  • @ApiNotImplementedResponse()
  • @ApiBadGatewayResponse()
  • @ApiServiceUnavailableResponse()
  • @ApiGatewayTimeoutResponse()
  • @ApiDefaultResponse()

The definition of the data model returned by the HTTP request can also be specified by specifying the type. Of course, this data model needs to describe each field through the decorator @ApiProperty.

import { ApiProperty } from '@midwayjs/swagger';

export class Cat {
@ApiProperty({ example: 'Kitty', description: 'The name of the Cat'})
name: string;

@ApiProperty({ example: 1, description: 'The age of the Cat' })
age: number;

@ApiProperty({
example: 'Maine Coon',
description: 'The breed of the Cat',
})
breed: string;
}

Swagger also supports extended fields with the prefix x-, you can use the @ApiExtension(x-..., {...}) decorator.

@ApiExtension('x-hello', { hello: 'world' })

When you do not want to define the model type by type, we can add additional schema type descriptions by adding @ApiExtraModel to the Controller or Model Class.

@ApiExtraModel(TestExtraModel)
@Controller()
class HelloController {
@Post('/:id', { summary: 'test'})
@ApiResponse({
status: 200
content: {
'application/json ': {
schema: {
properties: {
data: { '$ref': getSchemaPath(TestExtraModel)}
}
}
}
}
})
async create(@Body() createCatDto: CreateCatDto, @Param('id') id: number): Promise<Cat> {
return this.catsService.create(createCatDto);
}
}

// or
@ApiExtraModel(TestExtraModel)
class TestModel {
@ApiProperty({
item: {
$ref: getSchemaPath(TestExtraModel)
},
description: 'The name of the Catage'
})
one: TestExtraModel;
}

Generic returns data

The Swagger itself does not support generic data. As a type of Typescript, generics will be erased during the build period and cannot be read at runtime.

We can define it in some trick ways.

For example, we need to add some common package structure to the return value.

{
code: 200,
message: 'xxx',
data: any
}

To do this, we can write a method where the input parameter is the returned data and returns a wrapped class.

export function SuccessWrapper<T extends Type>(ResourceCls: T) {
class Successed {
@ApiProperty({ description: 'Status Code'})
code: number;

@ApiProperty({ description: 'message'})
message: string;

@ApiProperty({
type: ResourceCls
})
data: T;
}

return Successed;
}

We can implement our own return class based on this method.

class ViewCat extends SuccessWrapper(Cat) {}

When using, you can directly specify this class.

@Get('/:id')
@ApiResponse({
status: 200
description: 'The found record',
type: ViewCat
})
findOne(@Param('id') id: string, @Query('test') test: any): ViewCat {
// ...
}

Advanced usage

Routing label

Swagger labels paths. If no labels are defined in the Controller, they are grouped under default by default. Controller labels can be customized by @ApiTags([...]).

@ApiTags(['hello'])
@Controller('/hello')
export class HelloController {}

A description can be added to Tag through configuration.

// src/config/config.default.ts

export default {
swagger: {
tags: [
{
name: 'api',
description: 'API Document'
},
{
name: 'hello',
description: 'Other Router'
},
]
}
}

It can also be added to the routing method.

// ...
export class HomeController {
@ApiTags(['bbb'])
@Get('/')
async home(@Body() dto?: Photo): Promise<string> {
return 'Hello Midwayjs!';
}
}

Authorization verification

You can add authorization and authentication configurations to configure authentication methods. You can configure basic, bearer, cookie, oauth2, apikey, and custom.

basic

Enable basic authentication

// src/config/config.default.ts
export default {
// ...
swagger: {
auth: {
authType: 'basic',
},
},
}

Association Controller

@ApiBasicAuth()
@Controller('/hello')
export class HelloController {}

bearer

启用 bearer 验证(bearerFormat 为 JWT)

// src/config/config.default.ts
export default {
// ...
swagger: {
auth: {
authType: 'bearer',
},
},
}

Association Controller

@ApiBearerAuth()
@Controller('/hello')
export class HelloController {}

oauth2

Enable oauth2 authentication

// src/config/config.default.ts
export default {
// ...
swagger: {
auth: {
authType: 'oauth2',
flows: {
implicit: {
authorizationUrl: 'http://example.org/api/oauth/dialog',
scopes: {
'write:pets': 'modify pets in your account',
'read:pets': 'read your pets'
}
},
authorizationCode: {
authorizationUrl: 'https://example.com/api/oauth/dialog',
tokenUrl: 'https://example.com/api/oauth/token',
scopes: {
'write:pets': 'modify pets in your account',
'read:pets': 'read your pets'
}
},
},
},
},
}

Association Controller

@ApiOAuth2()
@Controller('/hello')
export class HelloController {}

Enable cookie authentication

// src/config/config.default.ts
export default {
// ...
swagger: {
auth: {
authType: 'cookie',
securityName: 'testforcookie',
cookieName: 'connect.sid',
},
},
}

Association Controller

@ApiCookieAuth('testforcookie')
@Controller('/hello')
export class HelloController {}

apikey

Enable cookie authentication

// src/config/config.default.ts
export default {
// ...
swagger: {
auth: {
authType: 'apikey',
name: 'api_key'
},
},
}

Association Controller

@ApiSecurity('api_key')
@Controller('/hello')
export class HelloController {}

custom verification

Custom verification method, you need to design your own parameter configuration.

// src/config/config.default.ts
export default {
// ...
swagger: {
auth: {
authType: 'custom',
name: 'mycustom'
// ...
},
},
}

Association Controller

@ApiSecurity('mycustom')
@Controller('/hello')
export class HelloController {}

Ignore routing verification

You can set @ApiExcludeSecurity to ignore validation of a route.

@Controller('/api')
@ApiSecurity('api_key')
class APIController {
// ...

@Get('/get_user')
@ApiExcludeSecurity()
async getUser() {
// ...
}
}

Ignore routing

Configuring @ApiExcludeController can ignore the entire Controller's routing.

@ApiExcludeController()
@Controller('/hello')
export class HelloController {}

Configure @ApiExcludeEndpoint to ignore individual routes.

@Controller('/hello')
export class HelloController {

@ApiExcludeEndpoint()
@Get()
async getUser() {
// ...
}
}

If you need to meet more dynamic scenarios, you can configure routing filters to filter in batches.

// src/config/config.default.ts
import { RouterOption } from '@midwayjs/core';

export default {
// ...
swagger: {
routerFilter: (url: string, options: RouterOption) => {
return url === '/hello/getUser';
}
},
}

routerFilter is used to pass in a filter function, including url and routerOptions two parameters. routerOptions contains basic routing information.

Whenever a route is matched, the routerFilter method will be automatically executed. When routerFilter returns true, it means that this route will be filtered.

Parameter configuration

Swagger components provide the same parameter configuration capability as the OpenAPI, which can be implemented through custom configuration.

The configuration items are as follows:

/**
* see https://swagger.io/specification/
*/
export interface SwaggerOptions {
/**
* default value: My Project
* https://swagger.io/specification/#info-object title field
*/
title?: string;
/**
* default value: This is a swagger-ui for midwayjs project
* https://swagger.io/specification/#info-object description field
*/
description?: string;
/**
* Default value: 1.0.0
* https://swagger.io/specification/#info-object version field
*/
version?: string;
/**
* https://swagger.io/specification/#info-object contact field
*/
contact?: ContactObject;
/**
* https://swagger.io/specification/#info-object license field
*/
license?: LicenseObject;
/**
* https://swagger.io/specification/#info-object termsOfService field
*/
termsOfService?: string;
/**
* https://swagger.io/specification/#openapi-object externalDocs field
*/
externalDocs?: ExternalDocumentationObject;
/**
* https://swagger.io/specification/#openapi-object servers 字段
*/
servers?: Array<ServerObject>;
/**
* https://swagger.io/specification/#openapi-object tags field
*/
tags?: Array<TagObject>;
/**
* Please refer to the https://swagger.io/specification/#security-scheme-object
*/
auth?: AuthOptions | AuthOptions[];
/**
* Default: /swagger-ui
* path to access swagger ui
*/
swaggerPath?: string;
/**
* ascii sorting route tags
* You can use 1-xxx, 2-xxx, 3-xxx to define tag
*/
tagSortable?: boolean;
/**
* Configuration Required in UI Display
* Please refer to the https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/configuration.md#display
*/
displayOptions ?: {
deepLinking?: boolean;
displayOperationId?: boolean;
defaultModelsExpandDepth?: number;
defaultModelExpandDepth?: number;
defaultModelRendering?: 'example' | 'model';
displayRequestDuration?: boolean;
docExpansion?: 'list' | 'full' | 'none';
filter?: boolean | string;
maxDisplayedTags?: number;
showExtensions?: boolean;
showCommonExtensions?: boolean;
useUnsafeMarkdown?: boolean;
tryItOutEnabled?: boolean;
};

documentOptions?: {
/**
* Custom operationIdFactory for generating operationId
* @default () => controllerKey_webRouter.methodKey
*/
operationIdFactory?: (
controllerKey: string,
webRouter: RouterOption
) => string;
};
}
/**
* Inherited from https://swagger.io/specification/#security-scheme-object
*/
export interface AuthOptions extends Omit<SecuritySchemeObject, 'type'> {
/**
* Type of verification right
* basic => http basic verification
* bear => http jwt verification
* cookie => cookie verification
* oauth2 => use oauth2
* apikey => apiKey
* custom => custom type
*/
authType: AuthType;
/**
* https://swagger.io/specification/#security-scheme-object type field
*/
type?: SecuritySchemeType;
/**
* authType = cookie can be modified by ApiCookie the name associated with the decorator
*/
securityName?: string;
/**
* authType = cookie can be modified, cookie name
*/
cookieName?: string;
}

Decorator list

All decorators of the component refer to the design of @nestjs/swagger and are prefixed with Api. All decorators are listed here:

DecoratorSupport mode
@ApiBodyMethod
@ApiExcludeEndpointMethod
@ApiExcludeControllerController
@ApiHeaderController/Method
@ApiHeadersController/Method
@ApiOperationMethod
@ApiPropertyModel Property
@ApiPropertyOptionalModel Property
@ApiResponsePropertyModel Property
@ApiQueryMethod
@ApiResponseMethod
@ApiTagsController/Method
@ApiExtensionMethod
@ApiBasicAuthController
@ApiBearerAuthController
@ApiCookieAuthController
@ApiOAuth2Controller
@ApiSecurityController
@ApiExcludeSecurityMethod
@ApiParamMethod
@ApiExtraModelController

UI rendering

Rendering from Swagger-ui-dist

By default, if the swagger-ui-dist package is installed, the component will call renderSwaggerUIDist to render swagger ui by default. If you need to pass the options of swagger-ui, you can pass the swaggerUIRenderOptions option.

// src/config/config.default.ts
import { renderSwaggerUIDist } from '@midwayjs/swagger';

export default {
// ...
swagger: {
swaggerUIRender: renderSwaggerUIDist,
swaggerUIRenderOptions: {
// ...
}
},
}

If you want to adjust the UI configuration, you can replace the default swagger-initializer.js with a custom file.

// src/config/config.default.ts
import { AppInfo } from '@midwayjs/core';
import { renderSwaggerUIDist } from '@midwayjs/swagger';
import { join } from 'path';

export default (appInfo: AppInfo) {
return {
// ...
swagger: {
swaggerUIRender: renderSwaggerUIDist,
swaggerUIRenderOptions: {
customInitializer: join(appInfo.appDir, 'resource/swagger-initializer.js'),
}
},
}
}

The content of the customized swagger-initializer.js is roughly as follows:

window.onload = function() {
window.ui = SwaggerUIBundle({
url: "/index.json",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout",
persistAuthorization: true,
});
};

The url points to the current swagger json and can be modified by yourself. For the complete swagger-ui configuration, please refer to [here](https://github.com/swagger-api/swagger-ui/blob/master/docs/usage /configuration.md).

Rendering from CDN addresses such as unpkg

If the swagger-ui-dist package is not installed, the renderSwaggerUIRemote method is automatically used for rendering, and the cdn resource is provided by unpkg.com by default.

// src/config/config.default.ts
import { renderSwaggerUIRemote } from '@midwayjs/swagger';

export default {
// ...
swagger: {
swaggerUIRender: renderSwaggerUIRemote,
swaggerUIRenderOptions: {
// ...
}
},
}

Only Swagger JSON is provided

If you only want to provide Swagger JSON, you can configure renderJSON to only render JSON without introducing the swagger-ui-dist package.

// src/config/config.default.ts
import { renderJSON } from '@midwayjs/swagger';

export default {
// ...
swagger: {
swaggerUIRender: renderJSON,
},
}

Frequently Asked Questions

summary or description in route annotations such as @Get do not take effect

When there is an @ApiOperation, the summary or description in the @ApiOperation will be used first, so you only need to write one in routing annotations such as @ApiOperation and @Get.