原文地址:https://dev.to/kabartolo/how-to-document-an-express-api-with-swagger-ui-and-jsdoc-50do 作者:Kate Bartolo
JSDoc 是一种流行的工具,用于从应用程序源代码的注释中生成文档。它有两个作用。首先,文档直接对查看源代码的任何人可见。其次,这些注释可以后期编译成完整的参考文档集。
Swagger 提供了一种用于展示这些文档的工具:Swagger UI。Swagger UI 可以根据 OpenAPI 规范定义创建一个网页。正如本教程将展示的那样,这些定义可以直接在 JSDoc 注释中使用 YAML 编写。
在本教程中,你将为 Express API 设置一个 Swagger UI 文档网页。然后,你可以在 API 的源代码中编写 JSDoc 注释以生成 OpenAPI 定义。最后,你将获得遵循 OpenAPI 规范的文档,并通过在 Express API 中添加自定义的/docs
端点进行展示。
先决条件
要完成本教程,你需要:
- 熟悉 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:API 设计和文档化的最佳实践
本教程使用了由 OpenAPI 定义的以下与 API 相关的术语和定义:
https://api.example.com/v1/users?role=admin&status=active
\________________________/\____/ \______________________/
服务器URL 端点 查询参数
路径
- 服务器 URL 或基本 URL:所有 API 端点的基本 URL:
localhost:3000
或example.com/api
- 端点路径:表示资源位置的路径(相对于基本 URL):
/users
或/users/1
- 操作:用于操作端点路径的 HTTP 方法:GET、POST、PUT、DELETE
- 资源:表示真实世界对象的信息(例如用户或书籍),通常由 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 API 的app.js
文件中,在所需模块列表下添加以下代码:
// app.jsconst
swaggerJSDoc = require('swagger-jsdoc');
const swaggerDefinition = {
openapi: '3.0.0',
info: {
title: 'Express API for JSONPlaceholder',
version: '1.0.0',
},
};
const options = {
swaggerDefinition,
// 包含OpenAPI定义的文件路径apis: ['./routes/*.js'],
};
const swaggerSpec = swaggerJSDoc(options);
swaggerDefinition
对象(即 OpenAPI 定义)定义了 API 的根信息。向swaggerDefinition
提供一些基本信息,例如 API 的title
和version
。稍后可以填写更多信息。
options
对象包含swaggerDefinition
对象和一个名为apis
的路径数组。这些是包含其他 OpenAPI 定义的文件的路径。这些文件路径应相对于 Express API 的根目录。在我们的例子中,定义将直接在/routes
文件中的 JSDoc 中编写。你可以逐个列出文件名,也可以使用通配符*
添加目录中的所有 JavaScript 文件,如上所示。
options
对象由swagger-jsdoc
用于在名为swaggerSpec
的变量中生成一个 OpenAPI 规范。此规范相当于 Swagger UI 通常用于创建文档页面的swagger.json
或swagger.yaml
文件。你将在下一步中将此对象传递给 Swagger UI。
重新启动 Express 服务器以确保没有错误。如果在此阶段遇到任何错误,请检查你的swagger-jsdoc
版本是否确切为5.0.1
。
1.3:创建 Swagger UI 文档页面
要为 Express API 创建 Swagger UI 页面,请在app.js
文件中包含swagger-ui-express
。然后,添加一个名为/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
提供了两个回调函数来设置端点:一个用于使用swaggerSpec
定义设置 Swagger UI,另一个用于将其提供给/docs
端点。
重新启动 Express 服务器,并在浏览器中导航到localhost:3000/docs
。
你将看到 Express API 的标题和版本号,以及 OpenAPI 版本号(3.0.0
)。由于我们还没有其他定义,你将看到一个“在规范中未定义任何操作!”的消息:
现在,你已经拥有了一个漂亮的 API 文档页面的起点!本教程的其余部分将提供有关 OpenAPI 定义的基本介绍。
步骤 2:定义 API 的根信息
你已经创建了一个 Swagger UI 文档页面,并准备开始编写文档。但是,在此之前,你应该为 API 添加更多的根定义。
返回到app.js
。请注意,info
对象映射到 OpenAPI 的 Info 对象,用于定义 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:编写文档
在/docs
端点处有一个 Swagger UI 文档页面,并且你已经拥有了 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
。你应该看到响应,一个示例值(使用你为每个属性提供的示例值),以及此响应返回的数据的模式:
接下来,通过添加我们已经涵盖的字段(summary
、description
和responses
)来定义GET /users/:id
路径:
// 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 中不起作用。
这个schema
中的data
对象包含一个单独的用户对象,而不是一个用户对象数组,但属性是相同的。
接下来,通过添加我们已经涵盖的字段(summary
、description
和responses
)来定义POST /users
路径:
// 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 定义的每个模型都可以作为一个单独的模式定义(或组件)进行文档化。为了为用户模型做到这一点,请在文件顶部的components/schemas
下添加一个User
模式定义:
// 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
。你将在POST /users
的请求体定义中看到你的NewUser
模式:
这涵盖了使用 JSDoc 注释生成 OpenAPI 定义的基本技术。
总结
你现在已经准备好为 Express API 生成完整的参考文档页面。你已经创建了一组基本的 OpenAPI 定义和一个显示它们的 Swagger UI 页面。如果你想要更多练习 OpenAPI 规范,你可以完成文档化jsonplaceholder-express-api
。
本教程还介绍了编写 OpenAPI 定义的基础知识。要完成你的文档,请参阅 OpenAPI 规范和 Swagger 文档。
使用 Apifox 管理 API 文档
Swagger 管理接口有时很不方便,缺乏团队间的分享协作,所以我更推荐使用 Apifox。
Apifox 是一个比 Postman 更强大的接口测试工具,Apifox = Postman + Swagger + Mock + JMeter,Apifox 支持调试 http(s)、WebSocket、Socket、gRPC、Dubbo 等协议的接口,并且集成了 IDEA 插件。在开发完接口后,可以通过 Apifox 的 IDEA 插件一键生成接口文档,多端同步,非常方便测试和维护。
将 Swagger 导出为 JSON
如下图所示,选择“Convert and save as JSON”,将 Swagger 文档导出为 JSON 文件。
将 Swagger 文件导入 Apifox
打开 Apifox,创建一个项目后,选择“项目设置->导入数据->OpenAPI/Swagger->文件导入”,将已导出的 Swagger 格式的 JSON 文件导入即可。
导入时,会有预览,可以选择导入全部,也可以选择性的导入接口。
导入成功之后,就可以选择一个环境来测试接口。如下图所示,接口成功返回数据:
知识扩展: