[译] 将现有的 API 从 REST 迁移到 GraphQL

将现有的 API 从 REST 迁移到 GraphQL

最近的六个月内我发现几乎每一场有关于 Web 开发的大会都谈论到了 GraphQL。也有大量与其相关的文章被发表。可是全部的这些几乎都是在讲 GraphQL 的基础概念或者是新特性,说得很表面。所以我打算谈谈我在真实大型系统中采用 GraphQL 的我的经验。前端

REST 有什么问题

REST(一如 SOAP)没有分离传输、安全和数据逻辑层面。这会带来不少问题。让咱们来看看其中的几个。react

GET 查询能力的低下

用 GET 语句进行复杂深刻的查询是不可能的。假设咱们须要查询用户。举一个很简单的例子:android

GET /users/?name=Homerios

而后想象一下,咱们须要查找名字是 Homer 或者 Marge 的用户。事情就变得有点棘手了。固然,咱们能够为这种需求定义一些分隔符。git

GET /users/?name=Homer|Margegithub

可是,不要忘记转义这些字符!而且牢记,若是有人的名字中包含 “|” 那么你就完蛋啦。若是要结合两个不一样的字段那么就更复杂了。更别说是须要同时知足上面两种条件的查询。算法

目前咱们通常都是使用字段来查询对应的内容。可是也时常须要用查询语句来传递一些服务数据。好比页码:数据库

GET /users/?name=Homer|Marge&limit=10&offset=20npm

按逻辑来讲,咱们后端的查询解析器应该会将 limit 和 offset 识别为数据库的字段,由于他们被声明为和 “name” 字段同级的参数名。后端

咱们能够发明咱们本身的语法或是用 POST 方法(这是不对的,由于这是一个幂等请求)可是这看起来像是在造轮子。

数据更新的问题

使用 PUT 发送整个对象是最简单的 REST 更新数据的方式。但显而易见的是,当你仅仅只须要更新 1 Mb 大小的对象中的一个字段时,这并非最有效的方式。

HTTP 还有一个 PATCH 方法。可是它有一个问题。用算法来定义 如何更新实体 并不简单。现有多个规范建议你应该如何去作,好比 RFC 6902,RFC 7396 以及许多自定义解决方案。

命名问题

我猜想每一个曾与 REST 打交道的开发者都明白这种感觉,当你不知道如何去命名你的新路由时。并不是全部的业务实例均可以被描述为资源。例如咱们想要搜索带有商店信息的商品。

GET /search?product_name=IPhone&shop_name=IStore

这里的资源是什么?商品?商店?搜索?

天哪,个人 API 再也不是 REST 风格了!

另外一个典型的例子即是用户登陆。这里的资源又是什么?Spoiler:这里没有资源,这里只是个远程过程调用而已。

后端处理 REST

app.post((req, res) => {
  const user = db.getUserByName(req.headers.name);
  const user = db.getUserByName(req.query.name);
  const user = db.getUserByName(req.path.name);
  const user = db.getUserByName(req.body.name);
});复制代码

这是一个 Express 路由的例子。这里咱们试图获取用户的 ID 来查找用户。让咱们看一看 API 函数一般应该是什么样子:

函数接收参数,进行特定的处理并返回特定的结果。

在这个 Express 路由的例子中咱们的参数是什么?一个巨大杂乱的 req 对象,而咱们仅须要其中很小的一部分数据。

固然,这也是 Express 的一个问题(准确的说是 Node 的 HTTP 模块的问题),可是这样的接口也是由于 HTTP 的实现逐步进化而产生的 - 请求参数能够在任何位置,因此若是你本人不知道它或者没有使用描述良好的文档时想要准确知道参数位置是不可能的。

这就是为何使用没有接口文档的 REST 是如此的痛苦。

GraphQL

在这里咱们假设你早就熟悉 GraphQL 的基础知识。若是没有,你能够从 Apollo 写的关于 GraphQL 基础知识的介绍开始。

正如咱们前面所展现的,REST 存在一些 GraphQL 所没有的设计上的问题。而且 GraphQL 有着巨大的发展潜力。

首先 GraphQL 提供 RPC 访问方式,这意味着你将不受客户端-服务端的交互限制。GraphQL 有它本身的类型系统,这意味再也不有使人误解的错误和漏洞。而且类型系统意味着你的客户端能够提供 item 级别的数据智能缓存。还拥有大量像是网络链接(游标和分页)、批处理、延时等的面向 Web 的特性。它 使你的客户端-服务端交互尽量的高效

可是 REST 仍然是业内标准

是的,不管咱们是否喜欢,REST 都是近几年 API 的主流形式。

可是咱们仍然能够为一些内部需求(好比对接一些高级客户端)去使用 GraphQL,其余的使用 REST。

为此,咱们须要将 REST 路径包装成 GraphQL 类型。这里有一些文章和例子(被提到最多的是 swapi-rest-graphql)关于从 REST 迁移到 GraphQL。可是它们建议使用自定义解析器,这没法知足拥有成百上千路径的大型项目。

在我最近的三个项目中我使用 Swagger 来描述 REST 接口。它或多或少都算是声明式接口描述的标准。坦白说我真的不知道那些编写庞大却毫无描述的接口的人们是如何作到的。

一方面咱们把 Swagger 做为声明式 REST 接口的标准,另外一方面也能够这么看 GraphQL,咱们能够看到它们其实很是类似,只是除此以外 Swagger 还尝试去描述 HTTP 细节和业务逻辑。它们都描述了传入参数和传出响应的类型。这意味着咱们能够在它们之间写适配器!

REST 路径是这样子的

GET /user/id

能够采用 GraphQL 类型。

因此如今咱们只需一个库来帮助咱们自动转换。下面这个就是!

github.com/yarax/swagg…

Swagger2graphQL 接收你的 Swagger schema 而后返回 GraphQL schema,同时解析程序将自动构建 HTTP 请求到已有的 REST 路径上。

它被构建为一个将拥有超过 150 个路径的真实大型系统迁移到 GraphQL 的副项目。咱们须要在作功和问题都最少的状况下尽快地迁移到 GraphQL。

只须要克隆资源库,运行

npm install && npm start

而后访问 http://localhost:3009/graphql

你会看到封装在 petstore.swagger.io/ Swagger 示例接口上的 GraphQL 接口。

并且,有了 Swagger 和 GraphQL 编写新的路径将变得十分方便。若是你早就熟悉 GraphQL,你可能会发现有时候类型描述看起来至关冗长,由于你须要去建立大量的隐式类型。Swagger2graphQL 能够自动完成这些步骤,你只须要在 Swagger schema 中建立一个新的带有声明的路径,一般这很简单。

若是你遇到任何困难或者有疑问请向我提 issue!

同时你也能够在 Twitter 上找到我


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOSReact前端后端产品设计 等领域,想要查看更多优质译文请持续关注 掘金翻译计划

相关文章
相关标签/搜索