本系列文已出版成書「NestJS 基礎必學實務指南:使用強大且易擴展的 Node.js 框架打造網頁應用程式」,感謝 iT 邦幫忙與博碩文化的協助。如果對 NestJS 有興趣、覺得這個系列文對你有幫助的話,歡迎前往購書,你的支持是我最大的寫作動力!
上一篇我們讓 API 的參數能夠順利顯示在 Swagger UI 中,在設計完參數之後,我們可以針對 API 做分類,甚至把請求與回應的相關內容也設計至 Swagger UI 上。
這個功能是用來將 API 進行分類,透過 @ApiTags
裝飾器就可以將特定的 Controller 打上標籤,在 Swagger UI 上更容易找到對應的 API。
我們修改一下 TodoController
的內容,將 @ApiTags
套用上去,並指定標籤為 Todo
:
import { Body, Controller, Get, Param, Post } from '@nestjs/common';
import { ApiBody, ApiTags } from '@nestjs/swagger';
import { TodoService } from './todo.service';
import { CreateTodoDto } from './dto/create-todo.dto';
// 添加標籤
@ApiTags('Todo')
@Controller('todos')
export class TodoController {
constructor(private readonly todoService: TodoService) {}
@Post()
createTodo(@Body() data: CreateTodoDto) {
return this.todoService.createTodo(data);
}
@ApiBody({ type: [CreateTodoDto] })
@Post('bulk')
createTodos(@Body() todos: CreateTodoDto[]) {
return todos.map((todo) => this.todoService.createTodo(todo));
}
@Get(':id')
getTodo(@Param('id') id: string) {
return this.todoService.getTodo(id);
}
}
透過瀏覽器查看 http://localhost:3000/api 會發現多了一個 Todo
的區塊,裡面有 TodoController
設計的 API:
這個功能讓 API 在 Swagger UI 上可以有一個介面去配置特定 Header 的內容,透過 @ApiHeader
裝飾器給定 name
屬性,就能在 UI 上看到該欄位。
修改一下 TodoController
的內容,在 getTodo
套用 @ApiHeader
裝飾器,並指定 name
為 X-Custom
:
import { Body, Controller, Get, Param, Post } from '@nestjs/common';
import { ApiBody, ApiHeader, ApiTags } from '@nestjs/swagger';
import { TodoService } from './todo.service';
import { CreateTodoDto } from './dto/create-todo.dto';
@ApiTags('Todo')
@Controller('todos')
export class TodoController {
constructor(private readonly todoService: TodoService) {}
@Post()
createTodo(@Body() data: CreateTodoDto) {
return this.todoService.createTodo(data);
}
@ApiBody({ type: [CreateTodoDto] })
@Post('bulk')
createTodos(@Body() todos: CreateTodoDto[]) {
return todos.map((todo) => this.todoService.createTodo(todo));
}
// 在 Swagger UI 上可以看到 X-Custom 的欄位可以填寫
@ApiHeader({
name: 'X-Custom',
description: 'Try to set custom header.',
})
@Get(':id')
getTodo(@Param('id') id: string) {
return this.todoService.getTodo(id);
}
}
透過瀏覽器查看 http://localhost:3000/api 會看到下方結果:
這個功能可以在 Swagger UI 上顯示該 API 回傳的各個 HttpCode 是什麼意思,透過 @ApiResponse
裝飾器給定 status
與 description
即可。
修改 TodoController
的內容,在 createTodo
上套用 @ApiResponse
並指定 status
為 HttpStatus.CREATED
:
import { Body, Controller, Get, HttpStatus, Param, Post } from '@nestjs/common';
import { ApiBody, ApiHeader, ApiResponse, ApiTags } from '@nestjs/swagger';
import { TodoService } from './todo.service';
import { CreateTodoDto } from './dto/create-todo.dto';
@ApiTags('Todo')
@Controller('todos')
export class TodoController {
constructor(private readonly todoService: TodoService) {}
// 在 Swagger UI 上可以看到狀態 201 的描述
@ApiResponse({
status: HttpStatus.CREATED,
description: 'The todo has been successfully created.',
})
@Post()
createTodo(@Body() data: CreateTodoDto) {
return this.todoService.createTodo(data);
}
@ApiBody({ type: [CreateTodoDto] })
@Post('bulk')
createTodos(@Body() todos: CreateTodoDto[]) {
return todos.map((todo) => this.todoService.createTodo(todo));
}
@ApiHeader({
name: 'X-Custom',
description: 'Try to set custom header.',
})
@Get(':id')
getTodo(@Param('id') id: string) {
return this.todoService.getTodo(id);
}
}
透過瀏覽器查看 http://localhost:3000/api 會看到狀態 201
的描述為 The todo has been successfully created.
:
事實上 Nest 有將各種狀態獨立包裝起來,共有以下這幾個,有興趣可以自行實驗看看:
@ApiOkResponse
@ApiCreatedResponse
@ApiAcceptedResponse
@ApiNoContentResponse
@ApiMovedPermanentlyResponse
@ApiBadRequestResponse
@ApiUnauthorizedResponse
@ApiNotFoundResponse
@ApiForbiddenResponse
@ApiMethodNotAllowedResponse
@ApiNotAcceptableResponse
@ApiRequestTimeoutResponse
@ApiConflictResponse
@ApiTooManyRequestsResponse
@ApiGoneResponse
@ApiPayloadTooLargeResponse
@ApiUnsupportedMediaTypeResponse
@ApiUnprocessableEntityResponse
@ApiInternalServerErrorResponse
@ApiNotImplementedResponse
@ApiBadGatewayResponse()
@ApiServiceUnavailableResponse
@ApiGatewayTimeoutResponse
@ApiDefaultResponse
如果 API 需要經過授權才能使用的話,在 Swagger 要如何添加授權機制呢?可以透過指定的裝飾器來實作,讓開發人員可以在 Swagger UI 上配置授權資訊來存取 API,非常實用的功能!下面我會挑選三個授權方法進行解說:
使用 Basic
授權方法,透過 @ApiBasicAuth
裝飾器將要授權的 API 套上此裝飾器,並呼叫 DocumentBuilder
實例中的 addBasicAuth
方法來將授權欄位開啟。
修改 TodoController
的內容,在 TodoController
帶上 @ApiBasicAuth
裝飾器:
import { Body, Controller, Get, HttpStatus, Param, Post } from '@nestjs/common';
import { ApiBasicAuth, ApiBody, ApiHeader, ApiResponse, ApiTags } from '@nestjs/swagger';
import { TodoService } from './todo.service';
import { CreateTodoDto } from './dto/create-todo.dto';
// Basic Auth
@ApiBasicAuth()
@ApiTags('Todo')
@Controller('todos')
export class TodoController {
constructor(private readonly todoService: TodoService) {}
@ApiResponse({
status: HttpStatus.CREATED,
description: 'The todo has been successfully created.',
})
@Post()
createTodo(@Body() data: CreateTodoDto) {
return this.todoService.createTodo(data);
}
@ApiBody({ type: [CreateTodoDto] })
@Post('bulk')
createTodos(@Body() todos: CreateTodoDto[]) {
return todos.map((todo) => this.todoService.createTodo(todo));
}
@ApiHeader({
name: 'X-Custom',
description: 'Try to set custom header.',
})
@Get(':id')
getTodo(@Param('id') id: string) {
return this.todoService.getTodo(id);
}
}
接著修改 main.ts
,呼叫 builder
的 addBasicAuth
方法:
import { NestFactory } from '@nestjs/core';
import { INestApplication } from '@nestjs/common';
import { DocumentBuilder, SwaggerCustomOptions, SwaggerModule } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
setupSwagger(app);
await app.listen(3000);
}
function setupSwagger(app: INestApplication) {
const builder = new DocumentBuilder();
const config = builder
.setTitle('TodoList')
.setDescription('This is a basic Swagger document.')
.setVersion('1.0')
.addBasicAuth() // 開啟授權欄位
.build();
const document = SwaggerModule.createDocument(app, config);
const options: SwaggerCustomOptions = {
explorer: true,
};
SwaggerModule.setup('api', app, document, options);
}
bootstrap();
透過瀏覽器查看 http://localhost:3000/api 會發現 Todo
區塊的 API 都有了鎖頭符號,這表示需要經過授權才能呼叫,可以透過畫面上的 Authorize
按鈕來填入授權資訊:
使用 Bearer
授權方法,透過 @ApiBearerAuth
裝飾器將要授權的 API 套上此裝飾器,並呼叫 DocumentBuilder
實例中的 addBearerAuth
方法來將授權欄位開啟。
修改 TodoController
的內容,在 TodoController
帶上 @ApiBearerAuth
裝飾器:
import { Body, Controller, Get, HttpStatus, Param, Post } from '@nestjs/common';
import { ApiBearerAuth, ApiBody, ApiHeader, ApiResponse, ApiTags } from '@nestjs/swagger';
import { TodoService } from './todo.service';
import { CreateTodoDto } from './dto/create-todo.dto';
// Bearer Auth
@ApiBearerAuth()
@ApiTags('Todo')
@Controller('todos')
export class TodoController {
constructor(private readonly todoService: TodoService) {}
@ApiResponse({
status: HttpStatus.CREATED,
description: 'The todo has been successfully created.',
})
@Post()
createTodo(@Body() data: CreateTodoDto) {
return this.todoService.createTodo(data);
}
@ApiBody({ type: [CreateTodoDto] })
@Post('bulk')
createTodos(@Body() todos: CreateTodoDto[]) {
return todos.map((todo) => this.todoService.createTodo(todo));
}
@ApiHeader({
name: 'X-Custom',
description: 'Try to set custom header.',
})
@Get(':id')
getTodo(@Param('id') id: string) {
return this.todoService.getTodo(id);
}
}
接著修改 main.ts
,呼叫 builder
的 addBearerAuth
方法:
import { NestFactory } from '@nestjs/core';
import { INestApplication } from '@nestjs/common';
import { DocumentBuilder, SwaggerCustomOptions, SwaggerModule } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
setupSwagger(app);
await app.listen(3000);
}
function setupSwagger(app: INestApplication) {
const builder = new DocumentBuilder();
const config = builder
.setTitle('TodoList')
.setDescription('This is a basic Swagger document.')
.setVersion('1.0')
.addBearerAuth() // 開啟授權欄位
.build();
const document = SwaggerModule.createDocument(app, config);
const options: SwaggerCustomOptions = {
explorer: true,
};
SwaggerModule.setup('api', app, document, options);
}
bootstrap();
透過瀏覽器查看 http://localhost:3000/api 效果與 Basic
相同,差別在於授權方式不同:
使用 OAuth2
授權方法,透過 @ApiOAuth2
裝飾器將要授權的 API 套上此裝飾器,並呼叫 DocumentBuilder
實例中的 addOAuth2
方法來將授權欄位開啟,但這裡需要特別設置 type
與 flow
參數來完成。
修改 TodoController
的內容,在 TodoController
帶上 @ApiOAuth2
裝飾器:
import { Body, Controller, Get, HttpStatus, Param, Post } from '@nestjs/common';
import { ApiBody, ApiHeader, ApiOAuth2, ApiResponse, ApiTags } from '@nestjs/swagger';
import { TodoService } from './todo.service';
import { CreateTodoDto } from './dto/create-todo.dto';
// OAuth2 Auth
@ApiOAuth2(['write', 'read', 'update'])
@ApiTags('Todo')
@Controller('todos')
export class TodoController {
constructor(private readonly todoService: TodoService) {}
@ApiResponse({
status: HttpStatus.CREATED,
description: 'The todo has been successfully created.',
})
@Post()
createTodo(@Body() data: CreateTodoDto) {
return this.todoService.createTodo(data);
}
@ApiBody({ type: [CreateTodoDto] })
@Post('bulk')
createTodos(@Body() todos: CreateTodoDto[]) {
return todos.map((todo) => this.todoService.createTodo(todo));
}
@ApiHeader({
name: 'X-Custom',
description: 'Try to set custom header.',
})
@Get(':id')
getTodo(@Param('id') id: string) {
return this.todoService.getTodo(id);
}
}
接著修改 main.ts
,呼叫 builder
的 addOAuth2
方法,將 type
指定為 oauth2
,flow
則是添加 implicit
參數並配置 authorizationUrl
、tokenUrl
以及 scopes
:
import { NestFactory } from '@nestjs/core';
import { INestApplication } from '@nestjs/common';
import { DocumentBuilder, SwaggerCustomOptions, SwaggerModule } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
setupSwagger(app);
await app.listen(3000);
}
function setupSwagger(app: INestApplication) {
const builder = new DocumentBuilder();
const config = builder
.setTitle('TodoList')
.setDescription('This is a basic Swagger document.')
.setVersion('1.0')
.addOAuth2({
type: 'oauth2',
flows: {
implicit: {
authorizationUrl: '<AUTHORIZATION_URL>', // 授權位址
tokenUrl: '<TOKEN_URL>', // 授權用 token
scopes: { // 權限選項
read: 'read',
write: 'write',
update: 'update',
delete: 'delete',
},
},
},
})
.build();
const document = SwaggerModule.createDocument(app, config);
const options: SwaggerCustomOptions = {
explorer: true,
};
SwaggerModule.setup('api', app, document, options);
}
bootstrap();
透過瀏覽器查看 http://localhost:3000/api 效果與 Basic
相同,但在授權資訊配置那裡會有差異,Swagger 會向該服務請求授權:
Swagger UI 設計的好壞會影響整體開發人員的效率,只要善用上一篇學到的 參數設計 以及本篇講解的 操作設計 與 授權設計,就可以大幅降低前後端的溝通成本,絕對是當今非常值得學習的工具!這裡附上今天的懶人包:
@ApiTags
將 API 進行分類。@ApiHeader
開啟指定的 Header 欄位。@ApiResponses
可以在 Swagger UI 上顯示該 API 回傳的各個 HttpCode 是什麼意思。@ApiOkResponse
。@ApiBasicAuth
、@ApiBearerAuth
以及 @ApiOAuth2
。DocumentBuilder
實例呼叫對應的授權機制方法,來開啟授權欄位。