如何使用 Swagger UI 和 JSDoc 编写 Express API 文档
JSDoc是一款流行的工具,可以根据应用源代码中的注释生成文档。这有两个目的。首先,任何查看源代码的人都可以直接访问该文档。其次,注释稍后可以编译成一套完整的参考文档。
Swagger提供了一个用于呈现此类文档的工具:Swagger UI。Swagger UI 根据OpenAPI 规范定义创建网页。正如本教程所示,这些定义可以直接以 YAML 格式写入 JSDoc 注释中。
在本教程中,您将为 Express API 设置一个Swagger UI文档网页。然后,您可以在 API 源代码中编写 JSDoc 注释来生成 OpenAPI 定义。最后,您将获得遵循 OpenAPI 规范的文档,并通过/docs
添加到 Express API 的自定义端点呈现:
先决条件
要完成本教程,您需要
-
熟悉 REST API 和Express
-
您的系统上安装了Node.js
-
在本地 Express 服务器上运行的基于 Express 的 REST API。如果您没有,可以安装本教程中使用的Express API 。它从JSONPlaceholder中检索用户数据。
要安装和运行示例 Express API,首先克隆存储库(替换test-api
为您选择的目录名称):
git clone https://github.com/kabartolo/jsonplaceholder-express-api test-api
接下来运行以下命令启动 Express 服务器(替换test-api
为刚刚创建的目录的名称):
cd test-api
npm install
npm run start
导航至localhost:3000
以查看 API。您应该会看到/users
和 的链接/users/1
。
导航到其中任何一个即可查看来自 JSONPlaceholder 的用户数据。
本教程中添加的代码可以在存储库的docs
分支中找到。
术语
OpenAPI是该规范的名称,而Swagger是实现该规范的工具集。请参阅Swagger 和 OpenAPI 之间有什么区别?
本教程使用OpenAPI定义的以下 API 相关术语和定义:
https://api.example.com/v1/users?role=admin&status=active
\________________________/\____/ \______________________/
server URL endpoint query parameters
path
- 服务器 URL 或基本 URL:所有 API 端点的基本 URL:
localhost:3000
或example.com/api
- 端点路径:表示资源位置的路径(相对于基本 URL):
/users
或/users/1
- 操作:用于操作端点路径的 HTTP 方法:GET、POST、PUT、DELETE
- Resource:表示现实世界对象(例如,用户或书籍)的信息,通常由 API 以 JSON 数据形式返回。在 Express 中,由数据库模型表示。
用于从 API 检索数据的完整 URL 是通过将端点添加到基本 URL 形成的:localhost:3000/users
。
步骤 1:设置应用程序
1.1:安装swagger-jsdoc
和swagger-ui-express
要从 JSDoc 注释创建 Swagger UI 页面,您需要一种方法将文档传递给 Swagger UI:
swagger-jsdoc
从 JSDoc 注释生成 OpenAPI 定义。swagger-ui-express
根据这些定义创建 Swagger UI 页面。
要安装swagger-jsdoc
并swagger-ui-express
运行 Express API,请运行
npm install swagger-jsdoc@5.0.1 --save-exact
npm install swagger-ui-express --save
本教程使用
swagger-jsdoc
版本5.0.1
。最新版本可能与本教程不兼容。
1.2:创建 API 规范
Swagger UI 根据一组 OpenAPI 定义创建文档页面。这些定义以YAML或JSON格式编写,用于描述 REST API。有关 OpenAPI 规范基本结构的更多信息,请参阅基本结构。
在您的 Express APIapp.js
文件中,在所需模块列表下方添加以下代码:
// app.js
const swaggerJSDoc = require('swagger-jsdoc');
const swaggerDefinition = {
openapi: '3.0.0',
info: {
title: 'Express API for JSONPlaceholder',
version: '1.0.0',
},
};
const options = {
swaggerDefinition,
// Paths to files containing OpenAPI definitions
apis: ['./routes/*.js'],
};
const swaggerSpec = swaggerJSDoc(options);
该swaggerDefinition
对象(即 OpenAPI 定义)定义了 API 的根信息。请向 提供一些基本信息swaggerDefinition
,例如API 的title
和version
;您可以稍后填写更多信息。
该options
对象包含此swaggerDefinition
对象和一个名为 的路径数组apis
。这些路径指向包含其他 OpenAPI 定义的文件。这些文件路径应相对于 Express API 的根目录。在本例中,定义将直接以 JSDoc 格式写入文件中/routes
。您可以单独列出文件名,也可以使用通配符分隔符*
添加目录中的所有 JavaScript 文件,如上所示。
该options
对象用于swagger-jsdoc
在名为 的变量中生成 OpenAPI 规范swaggerSpec
。此规范相当于Swagger UI 通常用于创建文档页面的swagger.json
或swagger.yaml
文件。您将在下一步中将此对象传递给 Swagger UI。
重启Express 服务器以确保没有错误。如果在此阶段遇到任何错误,请检查您的swagger-jsdoc
版本是否5.0.1
完全一致。
1.3:创建 Swagger UI 文档页面
要为您的 Express API 创建 Swagger UI 页面,请将其包含swagger-ui-express
在文件中。然后,添加一个名为(或您选择的任何名称)app.js
的端点路径:/docs
// app.js
// ...
const swaggerJSDoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');
// ...
var app = express();
app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
如上所示,swagger-ui-express
提供两个回调来设置端点:一个用于使用定义设置Swagger UI swaggerSpec
,另一个用于将其提供/docs
给端点。
重新启动Express 服务器,并在浏览器中导航至。localhost:3000/docs
您将看到 Express API 的标题和版本号,以及 OpenAPI 版本号 ( 3.0.0
)。由于我们还没有其他定义,您将看到“规范中未定义任何操作!”消息:
现在,您已经为 API 创建了一个漂亮的文档页面!本教程的其余部分将提供 OpenAPI 定义的基本介绍。
第 2 步:定义 API 的根信息
您已创建 Swagger UI 文档页面,现在就可以开始编写文档了。但首先,您需要为 API 添加更多根定义。
返回app.js
。请注意,该info
对象映射到 OpenAPI 的信息对象,以定义 API 的标题、描述、服务器列表、联系信息和路径列表。
以下是更完整定义的示例:
// app.js
const swaggerDefinition = {
openapi: '3.0.0',
info: {
title: 'Express API for JSONPlaceholder',
version: '1.0.0',
description:
'This is a REST API application made with Express. It retrieves data from JSONPlaceholder.',
license: {
name: 'Licensed Under MIT',
url: 'https://spdx.org/licenses/MIT.html',
},
contact: {
name: 'JSONPlaceholder',
url: 'https://jsonplaceholder.typicode.com',
},
},
servers: [
{
url: 'http://localhost:3000',
description: 'Development server',
},
],
};
如果您有生产服务器,请将 URL 和说明添加到servers
列表中。有关可添加到根定义的其他属性的更多信息,请参阅基本结构。
在OpenAPI 文档中,您会注意到还有一个paths
字段。您无需在此处指定路径定义,因为每个路径都在 JSDoc 注释中单独定义(将在下一步添加)。这些路径定义会被编译swagger-jsdoc
成一个paths
对象。
重启Express 服务器,然后再次在浏览器中导航localhost:3000/docs
到。你应该会在文档页面顶部看到更多关于你的 API 的信息:
您现在可以开始记录您的 Express 路线。
步骤 3:编写文档
有了端点上可用的 Swagger UI 文档页面/docs
以及 API 的完整根信息,您就可以开始编写路径定义。每个路径定义都对应 API 中的一个 Express 路由。它描述了操作和端点路径,例如GET /users
和DELETE /users/:id
。
3.1:记录路线
要记录/routes/users.js
,首先在第一个路由上方添加以 开头的注释@swagger
。然后添加一些关于该路由的基本信息:
// routes/users.js
/**
* @swagger
* /users:
* get:
* summary: Retrieve a list of JSONPlaceholder users
* description: Retrieve a list of users from JSONPlaceholder. Can be used to populate a list of fake users when prototyping or testing an API.
*/
router.get('/', function(req, res) {
//...
});
请注意,swagger-jsdoc
查找带有@swagger
或@openapi
标签的注释来创建 OpenAPI 定义。
如代码示例所示,添加端点路径/users
和操作get
(缩进两个空格)。Express 路由器函数中的路径get('/')
是相对于 的/users
,因此定义中的路径应该是/users
。
应该summary
简要描述此路线的目标。description
应该提供更多详细信息,例如何时或为何要使用此路线。
请务必使用两个空格(或四个空格)来缩进,而不是使用制表符。有关更多信息,请参阅YAML 语法。
重启Express 服务器,然后再次在浏览器中导航到 。你应该会在页面底部看到一个列表:localhost:3000/docs
GET /users
3.2:记录回复
您的用户可能想知道此 GET 请求成功后会返回什么(即状态码为200
)。要定义成功响应,请在路径定义中添加一个responses
对象和一个名为 的响应:200
// routes/users.js
/**
* @swagger
* /users:
* get:
* summary: Retrieve a list of JSONPlaceholder users.
* description: Retrieve a list of users from JSONPlaceholder. Can be used to populate a list of fake users when prototyping or testing an API.
* responses:
* 200:
* description: A list of users.
* content:
* application/json:
* schema:
* type: object
* properties:
* data:
* type: array
* items:
* type: object
* properties:
* id:
* type: integer
* description: The user ID.
* example: 0
* name:
* type: string
* description: The user's name.
* example: Leanne Graham
*/
router.get('/', function(req, res) {
//...
});
字段description
描述响应或返回的内容。content
字段描述内容类型(application/json
),而schema
描述响应对象。在我们的例子中,JSONPlaceholder 返回一个带有data
字段的对象,该字段包含您请求的数据。对于此响应,data
包含一个用户对象数组。目前只需添加一两个用户属性(例如id
和),以避免文件混乱。name
为每个属性添加一个真实示例值(例如'Leanne Graham'
);否则,Swagger UI 会创建一个通用示例,例如'string'
。
注意此架构中类型的定义方式。例如,要定义一个数组,请添加
type: array
一个items
字段。有关类型的更多信息,请参阅数据类型文档。
您也可以通过这种方式描述错误响应。有关可用于描述每个响应的字段的更多详细信息,请参阅 Swagger 的“描述响应”文档。
重启Express 服务器,然后再次在浏览器中导航localhost:3000/docs
到。您应该会看到响应、一个示例值(使用您为每个属性提供的示例值)以及此响应中返回的数据的架构:
接下来,GET /users/:id
通过添加我们已经介绍过的字段(summary
、description
和responses
)来定义路径:
// routes/users.js
/**
* @swagger
* /users/{id}:
* get:
* summary: Retrieve a single JSONPlaceholder user.
* description: Retrieve a single JSONPlaceholder user. Can be used to populate a user profile when prototyping or testing an API.
* responses:
* 200:
* description: A single user.
* content:
* application/json:
* schema:
* type: object
* properties:
* data:
* type: object
* properties:
* id:
* type: integer
* description: The user ID.
* example: 0
* name:
* type: string
* description: The user's name.
* example: Leanne Graham
*/
router.get('/:id', function(req, res) {
//...
});
这里,路径参数 ( id
) 被添加到端点路径:/users/{id}
。花括号 ( {}
) 用于标记端点路径中的路径参数。请注意,冒号样式 ( /users/:id
) 不适用于 Swagger(感谢@sherwinwater指出这一点!)。
data
这里的对象包含schema
单个用户对象而不是用户对象数组,但属性是相同的。
接下来,POST /users
通过添加我们已经介绍过的字段(summary
、description
和responses
)来定义:
// routes/users.js
/**
* @swagger
* /users:
* post:
* summary: Create a JSONPlaceholder user.
* responses:
* 201:
* description: Created
* content:
* application/json:
* schema:
* type: object
* properties:
* data:
* type: object
* properties:
* id:
* type: integer
* description: The user ID.
* example: 0
* name:
* type: string
* description: The user's name.
* example: Leanne Graham
*/
router.post('/', function(req, res) {
// ...
});
在这种情况下,成功的响应将是201
。它返回一个带有data
包含新用户的字段的对象。
您可以继续以相同的方式为其余路由添加路径定义。我们将在后续步骤中进行一些重构。
重启Express 服务器,然后在浏览器中再次导航到 。现在您将看到、以及您添加的任何其他路径定义localhost:3000/docs
的列表:GET /users/{id}
POST /users
3.3:记录请求
请求数据(例如参数和请求主体)也可以记录在 OpenAPI 定义中。例如,GET /users/:id
有一个id
参数,就应该记录下来。
要记录参数,parameters
请向路径定义添加一个字段:
// routes/users.js
/**
* @swagger
* /users/{id}:
* get:
* summary: Retrieve a single JSONPlaceholder user.
* description: Retrieve a single JSONPlaceholder user. Can be used to populate a user profile when prototyping or testing an API.
* parameters:
* - in: path
* name: id
* required: true
* description: Numeric ID of the user to retrieve.
* schema:
* type: integer
* responses:
* 200:
* ...
*/
router.get('/:id', function(req, res) {
//...
});
在此参数的定义中,in
定义参数的位置(在本例中,它是一个路径参数,因为它是路径的一部分)。您还可以添加name
、description
和 ,schema
以及参数是否required
。有关更多详细信息,请参阅描述参数。
重启Express 服务器,然后再次在浏览器中导航localhost:3000/docs
到。您将看到此路由的参数列表:
接下来,记录请求主体,以POST /users
描述在数据库中创建新用户所需的数据。为此,请向以下路径定义添加一个字段:requestBody
// routes/users.js
/**
* @swagger
* /users:
* post:
* summary: Create a JSONPlaceholder user.
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* name:
* type: string
* description: The user's name.
* example: Leanne Graham
* responses:
* 201:
* ...
*/
router.post('/', function(req, res) {
// ...
});
这会将请求体架构添加到此路径定义中。此示例显示name
可以在请求体中发送。您可以稍后为新用户添加更多属性。有关更多详细信息,请参阅描述请求体。
重启Express 服务器,然后再次在浏览器中导航到 。你会看到一个名为 的部分,其中包含你提供的架构:localhost:3000/docs
Request body
3.4:记录资源
您可能已经注意到,到目前为止,您在文档中已经多次重复了用户架构。为了避免这种重复,您可以在一个地方定义用户架构,然后在其他地方引用它。
Express API 定义的每个模型都可以单独记录为一个 Schema 定义(或组件)。对于用户模型,请User
在文件顶部的 下添加一个 Schema 定义components/schemas
:
// routes/users.js
/**
* @swagger
* components:
* schemas:
* User:
* type: object
* properties:
* id:
* type: integer
* description: The user ID.
* example: 0
* name:
* type: string
* description: The user's name.
* example: Leanne Graham
*/
然后,您可以使用以下方式引用该架构定义$ref
:
// routes/users.js
/**
* @swagger
* /users:
* get:
* summary: Retrieve a list of JSONPlaceholder users
* description: Retrieve a list of users from JSONPlaceholder. Can be used to populate a list of fake users when prototyping or testing an API.
* responses:
* 200:
* description: A list of users.
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: '#/components/schemas/User'
*/
router.get('/', function(req, res) {
//...
});
该$ref
路径使用JSON 引用表示法。该#
符号指示当前文档的根,其余嵌套值随后按顺序解析。有关更多信息,请参阅使用 $ref。
重启Express 服务器,然后在浏览器中再次导航到 。你的路径定义现在将使用此架构,你应该在页面底部看到 的架构定义:localhost:3000/docs
User
User
类似地,您可以定义一个NewUser
对象在请求主体中引用POST /users
。由于它包含模式中的部分字段(但不是全部)User
,因此您也可以使用它$ref
来避免它们之间的重复:
/**
* @swagger
* components:
* schemas:
* NewUser:
* type: object
* properties:
* name:
* type: string
* description: The user's name.
* example: Leanne Graham
* User:
* allOf:
* - type: object
* properties:
* id:
* type: integer
* description: The user ID.
* example: 0
* - $ref: '#/components/schemas/NewUser'
*/
该allOf
关键字将模型定义(在本例中NewUser
为包含name
属性的定义)与具有属性的对象连接起来id
。更多详情请参阅oneOf、anyOf、allOf 和 not 。
您现在可以NewUser
从请求主体定义中引用POST /users
:
/**
* @swagger
* /users:
* post:
* summary: Create a JSONPlaceholder user.
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/NewUser'
* responses:
* 201:
* ...
*/
router.post('/', function(req, res) {
// ...
});
重启Express 服务器,然后在浏览器中再次导航到 。您将在 的请求正文定义中localhost:3000/docs
看到您的架构:NewUser
POST /users
这涵盖了在 JSDoc 注释中生成 OpenAPI 定义的基本技术。
结论
现在,您已准备好为您的 Express API 生成完整的参考文档页面。您已创建一组基本的 OpenAPI 定义和一个显示这些定义的 Swagger UI 页面。如果您想进一步练习 OpenAPI 规范,可以完成文档的编写jsonplaceholder-express-api
。
本教程还介绍了编写 OpenAPI 定义的基础知识。要完善您的文档,请参阅OpenAPI 规范和Swagger 文档。
要查看包含本教程中添加的所有代码的版本jsonplaceholder-express-api
,请参阅存储库的docs
分支。