Skip to content

Commit

Permalink
如何用 OpenAPI 在 Express 中构建更好的 API (#902)
Browse files Browse the repository at this point in the history
* 如何用 OpenAPI 在 Express 中构建更好的 API

* Update how-to-build-explicit-apis-with-openapi.md

* Update how-to-build-explicit-apis-with-openapi.md

* finish
  • Loading branch information
hezean committed Mar 26, 2023
1 parent cf56345 commit c3e32da
Showing 1 changed file with 72 additions and 70 deletions.
142 changes: 72 additions & 70 deletions chinese/articles/how-to-build-explicit-apis-with-openapi.md
Original file line number Diff line number Diff line change
@@ -1,107 +1,109 @@
> - 原文地址:[How to Build Better APIs in Express with OpenAPI](https://www.freecodecamp.org/news/how-to-build-explicit-apis-with-openapi/)
> - 原文作者:[Alain PerkazAlain Perkaz](https://www.freecodecamp.org/news/author/aperkaz/)
> - 译者:
> - 译者:HeZean
> - 校对者:
![How to Build Better APIs in Express with OpenAPI](https://images.unsplash.com/photo-1546411649-8c3bb42e6008?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8c2VhcmNofDh8fGJ1aWxkfGVufDB8fHx8MTYxNzM5NDY2MQ&ixlib=rb-1.2.1&q=80&w=2000)
![如何用 OpenAPI 在 Express 中构建更好的 API](https://images.unsplash.com/photo-1546411649-8c3bb42e6008?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8c2VhcmNofDh8fGJ1aWxkfGVufDB8fHx8MTYxNzM5NDY2MQ&ixlib=rb-1.2.1&q=80&w=2000)

In this article, I will share how to build robust REST APIs in Express. First, I will present some of the challenges of building REST APIs and then propose a solution using open standards.
我将在这篇文章中分享在 Express 中构建鲁棒的 REST API 的方法。首先,我会介绍构建 REST API 的一些挑战,然后提出一个使用开放标准的解决方案。

This article won't be an introduction to [Node.js](https://nodejs.org/en/), [Express.js](https://expressjs.com/), or [REST API](/news/rest-apis/)s. Make sure to check out the links before diving deeper if you need a refresher. 🤿
本文并非一篇关于 [Node.js](https://nodejs.org/en/)[Express.js](https://expressjs.com/) [REST API](/news/rest-apis/) 的介绍。如果你需要复习,请在深入研究本文内容之前查看这些链接。🤿

I love the Node.js ecosystem due to its flexibility and ease of use. The community is vibrant, and in a matter of minutes, you can setup a REST API using the language you already know.
我喜欢 Node.js 那极具灵活性和易用性的生态。这个社区充满活力,并且你可以用你已经掌握的语言在几分钟内设置一个 REST API

There is great value in sharing the same programming language between an application's back-end and front-end. This makes it easier to navigate the codebase of an application with less [context switching](https://blog.rescuetime.com/context-switching/). Full-stack developers can move across the stack quickly, and [sharing code](https://betterprogramming.pub/sharing-logic-components-between-frontend-and-backend-repositories-6fdc1f9cb850) becomes a breeze.
在应用的前后端使用相同的编程语言是一件很有价值的事。这使我们在浏览代码库时可以减少[上下文切换](https://blog.rescuetime.com/context-switching/),从而变得更轻松。全栈开发者可以快速切换技术栈,[共享代码](https://betterprogramming.pub/sharing-logic-components-between-frontend-and-backend-repositories-6fdc1f9cb850)也变得轻而易举。

That said, as MVPs grow into full-blown production applications and development teams scale, this flexibility creates challenges too.
尽管如此,随着 MVP 成长为成熟的生产环境应用程序和开发团队规模的扩大,这种灵活性也带来了挑战。

## Challenges of Working with REST APIs
## 使用 REST API 的挑战

There are many challenges to face when codebases and teams grow, regardless of which tech stack you use.
无论你使用哪种技术栈,当代码库和团队规模增长时,都会面临许多挑战。

I'll narrow these challenges down to Express.js apps which contain business logic exposed over a REST API.
在本文中,我将描述通过 REST API 暴露业务逻辑的 Express.js 应用程序所带来的挑战,以小见大。

Regardless of the nature of the API consumers (webpages, mobile apps, third-party backends), they are likely to face one (or more) of the following challenges as they grow:
无论 API 消费者的性质如何(网页、移动应用、第三方后端),随着他们的成长,他们都可能面临以下一个(或多个)挑战:

### 1\. ⚠️ It's harder to make changes
### 1\. ⚠️ 更难做出改变

When the contract is not explicit, making changes on either side of the REST API becomes harder.
在文档不够明确时,在 REST API 的任何一方进行修改都变得更加困难。

For example, you may have a REST endpoint that returns a specific user's name. In the upcoming feature, you may need to modify it to return the age too. This may silently break the web application and mobile app.
举个例子,假设你有一个 REST 端点,可以返回一个特定的用户的名字。在即将新增的功能中,你可能需要修改这个 API 使其返回年龄。这可能会潜在地破坏网络应用和移动应用。

You can set up integration tests to mitigate this issue, but you will still heavily rely on the developers to manually cover all the edge cases. This takes lots of time and effort, and you are never 100% certain that the changes won't break the app.
你可以设置集成测试来一定程度上避免这个问题,但你仍然会严重依赖开发人员来手动覆盖所有的边界情况。这需要大量的时间和精力,而且你永远无法 100% 确定这些变化不会破坏应用程序。

### 2\. 📜 Lack of (updated) documentation
### 2\. 📜 缺少(及时更新的)文档

Documentation is another sensitive topic when building REST APIs. I am a firm believer that, in most cases, the code should serve as enough documentation.
文档是构建 REST API 时的另一个敏感话题。我坚信在大多数情况下,代码本身应该足以代替一部分文档。

That said, REST APIs can grow in complexity, and checking the security, parameters, and possible responses for each endpoint in the code becomes tedious and time-consuming. This slows down the speed of development, and bugs creep into the system.
也就是说,REST API 在开发中会变得越来越复杂,检查代码中每个端点的安全性、参数和可能的响应也随之变得繁琐且耗时。这就减慢了开发的速度,也给 bug 进入系统留下了隐患。

Even if the team is committed to manually keeping the documentation up to date in a separate document from the code, it's hard to be 100% certain that it reflects the code.
即使团队致力于在一个独立于代码的文档中手动保持文档的更新,也很难 100% 确保它反映了代码的情况。

### 3\. 📢 Public APIs
### 3\. 📢 公共 API

This won't apply to all apps, but an application may need to expose a set of functionalities to a third party in some cases. When doing so, the third party may build core functionalities on top of our exposed APIs.
这并不适用于所有的应用程序,但在某些情况下,一个应用程序可能需要向第三方暴露一系列的功能。对于这种情况,第三方有可能会在我们暴露的 API 之上构建核心功能。

This means that we can't modify those APIs at the same rate that we update our private APIs. The third-party application may break, and that's something we should avoid at all costs.
这意味着我们不能以更新我们的私有 API 的同样速度来修改这些公共 API。一旦修改了公共 API,第三方应用程序可能会因此崩溃,而这正是我们应该不惜一切代价避免的事情。

What the public APIs expose should be explicit and simple to develop against, to limit the amount of back and forth communication needed between internal and external developer teams.
公共 API 所暴露的内容应该是明确的,并且可以简单地进行开发,以限制内部和外部开发团队之间所需的来回沟通的数量。

### 4\. ✍️ Manual integration tests
### 4\. ✍️ 手动集成测试

When applications grow organically without a thorough plan, the chances are high that what the API provides and what the API consumer expects is buried deep into the code.
当应用程序的开发没有与之匹配的周密计划时,很有可能 API 所提供的内容和 API 消费者期望的内容被深埋在代码中。

This is not a big problem when you have a small number of endpoints for internal use. But as the API surface grows, modifying existing endpoints requires following breadcrumbs across the whole system to ensure that what the consumer expects to receive is equal to what's provided.
对于仅有少量的内部端点的系统来说,这并不是一个大问题。但随着 API 接口数量的增长,修改现有的端点需要在整个系统中遵循面包屑,以确保消费者期望得到的东西与提供的东西是相等的。

This can be mitigated by keeping integration tests between the parts of the system that talk to the REST API. But doing it manually is tremendous work and when done poorly, provides false security that the system will work properly.
这个问题可以通过对系统的不同部分之间进行集成测试来缓解。但是人工完成这件事的工作量非常巨大的,并且如果没做好的话,可能会在系统实际上不能正常工作的时候让开发人员误以为系统状态良好。

## Proposed solution
## 提出的解决方案

We have seen some of the inherent challenges that come with building REST APIs. In the following section we will build an example Express project that addresses those challenges using open standards.
我们已经看到了构建 REST API 所带来的固有挑战。在下一节中,我们将使用开放标准构建一个示例 Express 项目,以解决这些挑战。

### API standard specification
### API 标准规范

The challenges described in the previous section have been around for a long time, so it pays off to look into existing solutions, instead of re-inventing the wheel.
前面部分描述的挑战已经存在很长时间了,所以面对这个问题,我们最好查看现有的解决方案,而不是重新发明轮子。

There have been multiple attempts to standardize REST API definitions ([RAML](https://raml.org/), [JsonAPI](https://jsonapi.org/), [OpenAPI](https://www.openapis.org/)...). These projects have the shared goal making it easier for developers to define how their APIs behave, so servers and clients across multiple languages can 'speak a common language'.
许多标准尝试对 REST API 进行规范化定义([RAML](https://raml.org/)[JsonAPI](https://jsonapi.org/)[OpenAPI](https://www.openapis.org/)...)。这些项目的共同目标是使开发人员更容易定义他们的 API 行为,以便跨多种语言的服务器和客户端能够“共说一种语言”。

Having some sort of formal specification of the API solves many of the challenges, since in many cases, client SDKs, tests, mock servers and documentation can be auto generated from those specifications.
有了某种形式的 API 规范,可以解决许多挑战,因为在许多情况下,可以从这些规范自动生成客户端 SDK、测试、模拟服务器和文档。

One of my favorites is OpenAPI (formerly Swagger). It has a big community, and plenty of tooling for Express. This may not the be the best tool for every REST API project out there, so remember to do some extra research to make sure the tooling and support around the said specification makes sense in your case.
一种我最喜欢的规范是 OpenAPI (原名 Swagger)。它有一个很大的社区,并且有很多用于 Express 的工具。这可能不是所有 REST API 项目中的最佳工具,因此请在为你自己的项目选择规范之前进行额外的研究,以确保该规范的工具和支持对你的项目有帮助。

### Context for our example
### 示例的背景

For the sake of this example, let's suppose we are building a todo list management app. The user has access to a web app where they can fetch, create, edit and delete todos, which are persisted in the backend.
在这个示例中,假设我们正在构建一个待办事项列表管理应用。用户可以通过访问一个 web 应用来获取、创建、编辑和删除待办事项,这些待办事项被保存在后端。

In this case, the backend will be an Express.js app that will expose over a REST API the following functionalities:
在这个例子中,后端使用一个 Express.js 应用程序,它将通过 REST API 暴露以下功能:

- Fetch todos: **\[GET\] /todos**
- Create a todo: **\[POST\] /todos**
- Edit a todo: **\[PUT\] /todos/:id**
- Delete a todo: **\[DELETE\] /todos/:id**
- 获取待办事项: **\[GET\] /todos**
- 创建待办事项:**\[POST\] /todos**
- 编辑待办事项:**\[PUT\] /todos/:id**
- 删除待办事项:**\[DELETE\] /todos/:id**

This is an over-simplification of the functionalities that a todo management app will need, but will serve to show how we can overcome the challenges presented above in a real context.
对于一个真实的待办事项管理应用来说,上面的功能有点过度简化,但这有助于展示我们如何在实际情况下克服上面提出的挑战。

### Implementation
### 实现

Great, now that we have introduced open standards for API definitions and a context, let's implement an Express todos app tackling the previous challenges.
很好,现在我们已经介绍了 API 定义的开放标准和背景,让我们来实现一个 Express 待办事项应用,演示怎么解决前面的挑战。

We will be using an OpenAPI with the Express library [**express-openapi**](https://github.com/kogosoftwarellc/open-api/tree/master/packages/express-openapi). Note that this library provides advanced functionalities (response validation, authentication, middleware setup...) beyond the scope of this post.
我们将使用 Express [**express-openapi**](https://github.com/kogosoftwarellc/open-api/tree/master/packages/express-openapi) 的 OpenAPI。请注意,这个库提供的高级功能(响应验证、认证、中间件设置......)超出了本文的范围。

The complete code is available in **[this repository](https://github.com/aperkaz/express-open-api)**.
你可以在**[这个仓库](https://github.com/aperkaz/express-open-api)**中找到演示的完整代码。

1. Initialize a Express skeleton and initialize a Git repo:
1. 初始化一个 Express 框架,并初始化一个 Git 仓库:

`npx express-generator --no-view --git todo-app`
`cd ./todo-app`
`git init`
`git add .; git commit -m "Initial commit";`
```bash
npx express-generator --no-view --git todo-app
cd ./todo-app
git init
git add .; git commit -m "Initial commit";
```

2\. Add the OpenAPI Express library, **[express-openapi](https://github.com/kogosoftwarellc/open-api/tree/master/packages/express-openapi)**:
2.**[express-openapi](https://github.com/kogosoftwarellc/open-api/tree/master/packages/express-openapi)** 引入我们的程序:

`npm i express-openapi -s`

```
```javascript
// ./app.js

...
Expand All @@ -120,9 +122,9 @@ initialize({
module.exports = app;
```

3\. Add OpenAPI base schema.
3. 添加 OpenAPI 基础模型。

Note that the schema defines the type of a **Todo**, which will be referenced in the route handlers.
请注意,模型中定义了 **Todo** 的类型,将在路由处理程序中引用。

```javascript
// ./api/api-doc.js
Expand Down Expand Up @@ -154,9 +156,9 @@ const apiDoc = {
module.exports = apiDoc;
```

4\. Add route [handlers](https://github.com/kogosoftwarellc/open-api/tree/master/packages/express-openapi#getting-started).
4. 添加路由[处理程序](https://github.com/kogosoftwarellc/open-api/tree/master/packages/express-openapi#getting-started)

Each handler declares which operations it supports (GET, POST...), the callbacks for each operation, and the **apiDoc** OpenAPI schema for that handler.
每个处理程序都声明它支持哪些操作(GETPOST ...),对每个操作的回调,以及该处理程序的 **apiDoc** OpenAPI 模型。

```javascript
// ./api/paths/todos/index.js
Expand Down Expand Up @@ -274,13 +276,13 @@ module.exports = function () {
};
```

5\. Add autogenerated documentation, **[swagger-ui-express](https://github.com/scottie1984/swagger-ui-express)**:
5. 添加自动生成的文档,**[swagger-ui-express](https://github.com/scottie1984/swagger-ui-express)**

```
```bash
npm i swagger-ui-express -s
```

```
```javascript
// ./app.js

...
Expand All @@ -299,24 +301,24 @@ app.use(
module.exports = app;
```

And here's what we'll get:
这就是我们最终获得的效果:

![](https://www.freecodecamp.org/news/content/images/2021/04/image-23.png)

Auto-generated SwaggerUi, at http://localhost:3030/api-documentation
这个 SwaggerUi 是自动生成的,你可以在 http://localhost:3030/api-documentation 访问它。

🎉 **Congratulations!**
🎉 **恭喜!**

If you have made it this far, you should have a fully functioning Express application, fully integrated with OpenAPI.
当你进行到文章的这里时,你应该创建好了一个完全可运行的 Express 应用程序,其与 OpenAPI 完全集成。

Using the schema available in _[http://localhost:3030/api-docs](http://localhost:3030/api-docs)_ we can now easily generate [tests](https://nordicapis.com/generating-web-api-tests-from-an-openapi-specification/), a [mock server](https://github.com/stoplightio/prism), [types](https://github.com/drwpow/openapi-typescript) or even a [client](https://phrase.com/blog/posts/using-openapi-to-generate-api-client-code/)!
现在,通过使用在 _[http://localhost:3030/api-docs](http://localhost:3030/api-docs)_ 中定义的模型,我们可以轻松生成[测试](https://nordicapis.com/generating-web-api-tests-from-an-openapi-specification/)[模拟服务器](https://github.com/stoplightio/prism)[类型](https://github.com/drwpow/openapi-typescript),甚至[客户端](https://phrase.com/blog/posts/using-openapi-to-generate-api-client-code/)

## Conclusion
## 总结

We scratched only the surface of whats possible with OpenAPI. But I hope the article shed some light on how a standard API definition schema can help with visibility, testing, documentation, and overall confidence when building REST APIs.
我们只是浅浅涉猎了 OpenAPI 所能做到的事情。但是我希望这篇文章能够让你了解标准 API 定义模式是如何在可见性、测试、文档和整体置信度方面帮助构建 REST API 的。

Thanks for sticking around until the end!
谢谢你看到最后!

I am currently building [__**taggr**__](https://taggr.ai/)_,_ a cross-platform desktop application that enables users to ****rediscover**** their digital ****memories**** while keeping their ****privacy****.
我目前正在构建 [__**taggr**__](https://taggr.ai/),这是一个跨平台的桌面应用程序,它在帮助用户**重新发现**他们的数字记忆的同时保持他们的**隐私**

The open-alpha is coming soon to Linux, Windows, and Mac OS. Make sure to check the [webpage](https://taggr.ai/) and [signup](https://taggr.us18.list-manage.com/subscribe/post?u=482d473aa1e4dedadc89fb3e2&id=aa6a10c164) so you don't miss it!
LinuxWindows 和 macOS 平台上的 alpha 版本即将推出。请查看[网页](https://taggr.ai/)[登记](https://taggr.us18.list-manage.com/subscribe/post?u=482d473aa1e4dedadc89fb3e2&id=aa6a10c164),以免错过!

0 comments on commit c3e32da

Please sign in to comment.