分分钟将 REST 转换为 GraphQL
作者丨Noorain Panjwani
译者丨张卫滨
策划 | 万佳
这个指南教你在不修改任何代码的情况下,完成从 REST 到 GraphQL 的迁移。这样,GraphQL 就能让你的 REST 真正休息一下了!
GraphQL 的支持者在宣传推广 GraphQL 方面已经做得非常好了。出于对他们努力的尊重,我就不再深入介绍细节,只是稍微总结一下:
GraphQL 允许我们在单个请求中获取多个资源;
通过精确描述所需数据,GraphQL 解决了 REST 过度抓取数据的问题;
GraphQL 借助在一个请求中获取相关的数据,解决了前端 N+1 查询的问题。
本指南中,我会介绍大多数倡导 GraphQL 的人所忽略的一个点,就是“我们在 REST 上有了巨大投入”。这意味着:
我们大多数的服务都是基于 REST 的;
我们更愿意编写 REST 服务;
我们希望支持使用 REST API 的现有客户端。
虽然有许多文章帮我们从 REST 迁移到 GraphQL,但这些做法都迫使我们更改现有的代码库或在 REST 服务前面编写新的代码库。
如果它还能运行,就别去碰它。
这难道不是编程的第一规则吗?
迁移可能非常痛苦,尤其是面对巨大的代码库时,更会令人望而生畏。随时存在将已有功能破坏掉的可能性。
我们为什么不能继续保持 REST 呢?我们天性懒惰,都喜欢用简单的技巧和容易的解决方案。
如果有一种方式,让我们原样保持 REST 服务,且无需任何代码变更就能在其之上实现一个 GraphQL 层,那会怎样?听起来很神奇, Space Cloud 就能帮我们实现这一切
简而言之,Space Cloud 是一个开源的 Web 服务器,它能在数据库和微服务之上即时提供 GraphQL 和 REST API。
Space Cloud 最酷的一点在于,所有 API 都是实时的。我们可以选择订阅数据库的变更。在实现实时应用时,该功能非常便利。
本指南中,我们会用 Space Cloud 的 remote service 模块将 REST 服务迁移成 GraphQL。
基于 REST 之上的 GraphQL 架构最终如下图所示:
我们的应用将会发起对 Space Cloud 的 GraphQL 查询,该请求又会访问服务器上的 REST 端点。在该场景中,Space Cloud 会作为 GraphQL 代理或 API 网关。
你可能注意到,Space Cloud 是一个单独的 GraphQL 层,位于 REST 服务之上。这种方式的优点在于 REST 服务依然能够保持原样,已有的客户端可以直接使用它们。这样,从 REST 服务迁移至 GraphQL 不会破坏旧的客户端。
接下来,让我们开始。
本指南中,我们将会构建一个简单的算数服务,它包含如下端点:
求和计算端点:POST /adder
翻倍计算端点:GET /doubler/:num
求和计算端点将会返回两个数的和,这两个数是通过请求的 body 获取到的。翻倍计算端点将会对其接收到的值翻倍,初始值是通过 URL 路径参数获取到的。
现在,我们开始编写 REST 服务。在这里,我们使用 NodeJS 和 Express 来编写 REST 服务。
注意:你可以使用任意语言和框架来编写服务,只要它支持 HTTP 即可,因为这是 Space Cloud 用来与你的 REST 服务进行通信的协议。
首先,创建一个文件夹作为工作目录。
创建 NPM 项目
npm init -y
安装 Express
npm install --save express
编写 express
服务器创建名为 index.js 的文件,并复制粘贴如下代码:
var express = require("express");
var app = express();
app.use(express.json());
app.post("/adder", function(req, res) {
const num1 = req.body.num1;
const num2 = req.body.num2;
const response = { result: num1 + num2 };
res.status(200).send(JSON.stringify(response));
});
app.get("/doubler/:num", function(req, res) {
const num = req.params.num;
const response = { result: num * 2 };
res.status(200).send(JSON.stringify(response));
});
var server = app.listen(5000, function () {
console.log("app running on port:", server.address().port);
});
可以看到,代码非常简单直接。我们只是使用 ExpressJS 创建了一个 HTTP 服务器并监听 5000 端口。
如前文所示,服务器包含两个端点:
求和计算端点:我们预期从 POST 的 body 中接收到两个数字,即 num1 和 num2,接下来所做的就是返回这两个数字的和。
翻倍计算端点:我们将通过 URL 路径参数得到的数字乘以 2,然后返回。对于服务来讲,我们做这些就足够了。
要运行服务,我们只需执行如下命令即可:
node index.js
我们让 REST 服务启动并运行了起来。接下来,启动 Space Cloud 并通过 GraphQL 来使用 REST 服务。
我们需要为自己的操作系统下载 Space Cloud 二进制文件,也可以通过其源码直接进行构建。如果从源码构建的话,需要 go 1.13.0 或更高版本。
可以通过以下链接下载对应操作系统的二进制文件:
Mac
Linux
Windows
下载后,我们解压压缩包。
对于 Linux/Mac:unzip space-cloud.zip && chmod +x space-cloud
对于 Windows:右键点击压缩包并选择“解压到此处”。
为确保二进制文件的正确性,在二进制文件的解压目录下,运行如下命令:
对于 Linux/Mac:./space-cloud -v
对于 Windows:space-cloud.exe -v
它将会展现如下输出:
space-cloud-ee version 0.13.0
要以 dev 模式启动 Space Cloud,可以复制粘贴如下命令并点击回车键:
对于 Linux/Mac:./space-cloud run --dev
对于 Windows:space-cloud.exe run --dev
在 Space Cloud 启动时,我们会看到如下所示的输出:
Creating a new server with id auto-1T5fA9E1B2jeNUbV8R0fOPubRng
Starting http server on port: 4122
Hosting mission control on http://localhost:4122/mission-control/
Space cloud is running on the specified ports :D
注意:--dev 标记会告诉 Space Cloud 以 dev 模式运行(所以,admin UI 不会要求输入用户名和密码)。
我们注意到,Space Cloud 在工作目录生成一个 config.yaml 文件。
Space Cloud 需要该文件来完成它的功能。这个文件用来加载一些信息,包括要连接的 REST 服务器以及它们的端点。Space Cloud 有自己的 Mission Control(admin UI),借助它能够快速完成配置。
打开 Mission Control导航至 http://localhost:4122/mission-control,可以打开 Mission Control。
创建项目点击 Create a Project 按钮,打开如下界面:
为你的项目设置一个 name。
在这里选择什么数据库无关紧要,因为我们不会用到它。
点击 Next 创建项目。
导航至 Mission Control 的 Remote Services 区域。
点击 Add first remote service 按钮来打开如下的表单:
将服务名设置为 arithmetic,并将服务的 URL 设置为:
http://localhost:5000
添加完远程服务之后,在远程服务的表格中,我们应该就能看到它:
点击 Actions 列中的 Add 按钮,将会打开服务页面。
点击 Add first remote endpoint 按钮,打开如下所示表单:
为求和计算端点添加如下内容:
Name:adder
Method:POST
Path:/adder
再次点击“Add”按钮并添加 doubler 端点:
Name:doubler
Method:GET
Path:/doubler/{args.num}
注意:现在不要担心{args.num}部分,只需要确保将 Method 设置为 GET 即可。
现在,我们添加了 REST 和两个端点到 Space Cloud 中,接下来,我们该使用统一的 GraphQL API 对其进行查询。
跳转至 Explorer 区域:
尝试在 GraphiQL explorer 中运行如下的 GraphQL 查询:
{
adder(num1: 10, num2: 20) {
result
}
}
将会看到如下所示的响应:
{
"adder": {
"result": 30
}
在得到上述 GraphQL 查询后,Space Cloud 会向 REST 服务发送如下的请求:
Method:POST
Path:/adder
Request Body:
{
"num1": 10,
"num2": 20
}
这意味着我们传递给 GraphQL 查询的参数以 Request Body 的形式传递给了 REST 服务。
接下来,我们尝试使用如下的 GraphQL 查询来访问 doubler 端点:
{
doubler(num: 50) {
result
}
}
GraphQL 查询会被 Space Cloud 翻译成为对 REST 的调用,如下所示:
GET /doubler/50
如果你还记得的话,我们添加到 Space Cloud 的 doubler 端点是这样的:
/doubler/{args.num}
基于该端点,Space Cloud 能够知道它要从 GraphQL 中获取一个参数 num,并使用它作为变量来形成路径 /doubler/50。
成功调用后,我们会看到如下所示的响应:
{
"doubler": {
"result": 100
}
}
如果成功遵循这个指南的话,我们会有一个奖励!这个 REST 到 GraphQL 的转换为我们解锁了一个超级强大的功能:服务链(Service Chaining)。
让我们来看一个场景:
我们想要使用 the adder service 对两个数字求和;
我们想把从 the adder service 得到的结果翻倍。
如果我们在客户端代码中使用 REST 的话,上述任务将会如下所示:
注意,我们从前端发出两个请求,就意味着往返时间会翻倍。它会导致较慢的响应时间和较差的用户体验。
现在,如果我们将客户端从 REST 切换为使用 Space Cloud 的 GraphQL,那么我们的请求将会如下所示:
注意,在这里,我们只从前端发起了一次 GraphQL 查询到后端(Space Cloud)。而 Space Cloud 发起两次请求到 REST 服务器以满足该请求。但是,这些请求(从 Space Cloud 到我们的服务器)的往返时间是微不足道的,因为它们位于同一个网络中。
要完成上述任务,到 Space Cloud 的 GraphQL 查询如下所示:
{
adder(num1: 10, num2: 20) {
doubler(num: "adder.result") {
result
}
}
}
请注意,我们是如何在得到 adder 服务的响应后调用 doubler 服务的,而且我们将 adder 服务的 result 以参数形式传递给 doubler 服务。
查询结果将会如下所示:
{
"adder": {
"doubler": {
"result": 60
}
}
}
可以猜到,我们得到的结果是 60,即 ((10 + 20) * 2)。
小提示:如果你想并行执行两个不相关 REST 服务的话,我们可以在一个请求中完成,如下所示:
{
adder(num1: 10, num2: 20) {
result
}
doubler(num: 50) {
result
}
}
我把这个查询会得到什么响应作为作业留给读者。
首先,鼓励一下自己,因为你完整地读完了该指南。
通过该指南,我们学到:
从 REST 迁移到 GraphQL 不需要更改代码。
我们不需要在 REST 和 GraphQL 之间进行非此即彼的选择。我们可以在同一个应用程序中同时支持 REST 和 GraphQL。
借助 Space Cloud 来使用 GraphQL 会带来网络方面的收益,有助于减少往返时间。
除了从 REST 迁移到 GraphQL(如跨数据库连接)之外,我们还可以用 Space Cloud 做更多事情。
点个在看少个 bug👇