[关闭]
@levinzhang 2020-01-27T18:06:02.000000Z 字数 5751 阅读 597

分分钟将REST转换为GraphQL

摘要

在迁移至GraphQL解决方案的时候,我们经常会面临如何处理已有REST资产的问题,本文借助Space Cloud给出了无需修改任何代码就能以GraphQL方式访问REST服务的方案。


本文最初发表于DZone博客站点,经原作者Noorain Panjwani授权由InfoQ中文站翻译分享。

随着GraphQL在API领域的飞速发展,你可能想要知道如何在不破坏任何东西的情况下将现有的REST API迁移到GraphQL。本指南将会帮助你在不修改任何代码的情况下,完成REST到GraphQL迁移的任务,这样GraphQL就能让你的REST真正休息一下了。

从REST到GraphQL

GraphQL的支持者在宣传推广GraphQL方面已经做得非常好了。出于对他们努力的尊重,我不再深入介绍细节,而只是稍微总结一下:

在本指南中,我所介绍的将是大多数倡导GraphQL的人所忽略的一个方面,那就是“我们已经在REST方面有了巨大的投入”。这意味着:

虽然有许多文章帮助我们从REST迁移到GraphQL,但这些做法都迫使我们更改现有的代码库或在REST服务前面编写新的代码库。

注意:你可能还会喜欢该文:为什么以及何时使用GraphQL

但是,稍等一下……

如果它还能运行,那么就别去触碰它。

难道这不是编程的第一规则吗?

迁移可能是非常痛苦的,面对巨大的代码库时更会令人望而生畏。随时存在将已有的功能破坏掉的可能性。

我们为什么不能继续保持REST呢?

面对现实吧,我们天生都是懒惰的。我们都喜欢简单的技巧和容易的解决方案。

如果能有那么一种方式,让我们原样保持REST服务,并且无需任何代码变更就能在它之上实现一个GraphQL层,那么将会怎样呢?听起来很神奇,对吧?Space Cloud就能够帮助我们让这一切变成现实。

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不会破坏旧的客户端。

非常棒!我们已经理解了什么是Space Cloud以及它如何与我们的应用共处。接下来,让我们开始行动吧!

我们将要构建什么呢

在本指南中,我们将会构建一个简单的算数服务,它包含如下的端点:

求和计算端点将会返回两个数的和,这两个数是通过请求的body获取到的。翻倍计算端点将会对其接收到的值翻倍,初始值是通过URL路径参数获取到的。

非常好!现在,我们开始构建吧。

第一步:编写服务

现在,我们开始编写REST服务。在这里,我们使用NodeJS和Express来编写REST服务。

注意:你可以使用任意语言和框架来编写服务,只要它支持HTTP即可,因为这是Space Cloud用来与你的REST服务进行通信的协议。

首先,创建一个文件夹作为工作目录。

创建NPM项目

  1. npm init -y

安装Express

  1. npm install --save express

编写express服务器

创建名为index.js的文件,并复制粘贴如下的代码:

  1. var express = require("express");
  2. var app = express();
  3. app.use(express.json());
  4. app.post("/adder", function(req, res) {
  5. const num1 = req.body.num1;
  6. const num2 = req.body.num2;
  7. const response = { result: num1 + num2 };
  8. res.status(200).send(JSON.stringify(response));
  9. });
  10. app.get("/doubler/:num", function(req, res) {
  11. const num = req.params.num;
  12. const response = { result: num * 2 };
  13. res.status(200).send(JSON.stringify(response));
  14. });
  15. var server = app.listen(5000, function () {
  16. console.log("app running on port:", server.address().port);
  17. });

可以看到,代码非常简单直接。我们只是使用ExpressJS创建了一个HTTP服务器并监听5000端口。

如前文所示,服务器包含了两个端点:

对于服务来讲,我们做这些就已经足够了。

第二步:启动服务

要运行服务,我们只需执行如下的命令即可:

  1. node index.js

非常好!我们已经让REST服务启动并运行了起来。接下来,我们要启动Space Cloud并通过GraphQL来使用REST服务。

第三步:下载Space Cloud

我们需要为自己的操作系统下载Space Cloud二进制文件,也可以通过其源码直接进行构建。如果从源码构建的话,需要go 1.13.0或更高版本。

可以通过以下链接下载对应操作系统的二进制文件:

下载后,我们需要解压压缩包。

对于Linux/Macunzip space-cloud.zip && chmod +x space-cloud

对于Windows:右键点击压缩包并选择“解压到此处”。

为了确保二进制文件的正确性,在二进制文件的解压目录下,运行如下的命令:

对于Linux/Mac./space-cloud -v

对于Windowsspace-cloud.exe -v

它将会展现如下的输出:

  1. space-cloud-ee version 0.13.0

第四步:下载Space Cloud

要以dev模式启动Space Cloud,可以复制粘贴如下的命令并点击回车键:

对于Linux/Mac./space-cloud run --dev

对于Windowsspace-cloud.exe run --dev

在Space Cloud启动的时候,我们会看到如下所示的输出:

  1. Creating a new server with id auto-1T5fA9E1B2jeNUbV8R0fOPubRng
  2. Starting http server on port: 4122
  3. Hosting mission control on http://localhost:4122/mission-control/
  4. Space cloud is running on the specified ports :D

注意:--dev标记会告诉Space Cloud以dev模式运行(所以,admin UI不会要求输入用户名和密码)。

第五步:配置Space Cloud

我们可以注意到,Space Cloud在工作目录生成了一个config.yaml文件。

Space Cloud需要该文件来完成它的功能。这个文件用来加载一些信息,包括要连接的REST服务器以及它们的端点。

Space Cloud有自己的Mission Control(admin UI),借助它能够快速完成配置。

打开Mission Control

导航至http://localhost:4122/mission-control,可以打开Mission Control。

注意:如果你不是在本地Space Cloud的话,那么需要将localhost替换为实际的地址。

创建项目

点击Create a Project按钮打开如下的界面:

为你的项目设置一个name

在这里选择什么数据库无关紧要,因为我们不会用到它。

点击Next创建项目。

第六步:添加远程服务到Space Cloud中

导航至Mission Control的Remote Services区域。

点击Add first remote service按钮来打开如下的表单:

将服务名设置为arithmetic,并将服务的URL设置为:

  1. http://localhost:5000

添加完远程服务之后,在远程服务的表格中,我们应该就能看到它:

点击Actions列中的Add按钮,将会打开服务页面。

点击Add first remote endpoint按钮以打开如下所示的表单:

为求和计算端点添加如下的内容:

再次点击“Add”按钮并添加doubler端点:

注意:现在不要担心{args.num}部分,只需要确保将Method设置为GET即可。

第七步:通过GraphQL来查询REST服务

现在,我们已经添加了REST和两个端点到Space Cloud中,接下来,我们该使用统一的GraphQL API对其进行查询。

跳转至Explorer区域:

尝试在GraphiQL explorer中运行如下的GraphQL查询:

  1. {
  2. adder(num1: 10, num2: 20) @arithmetic {
  3. result
  4. }
  5. }

将会看到如下所示的响应:

  1. {
  2. "adder": {
  3. "result": 30
  4. }
  5. }

在得到上述GraphQL查询后,Space Cloud会向REST服务发送如下的请求:

  1. {
  2. "num1": 10,
  3. "num2": 20
  4. }

这意味着我们传递给GraphQL查询的参数以请求体的形式传递给了REST服务。

接下来,我们尝试使用如下的GraphQL查询来访问doubler端点:

  1. {
  2. doubler(num: 50) @arithmetic {
  3. result
  4. }
  5. }

GraphQL查询会被Space Cloud翻译成为对REST的调用,如下所示:

  1. GET /doubler/50

如果你还记得的话,我们添加到Space Cloud的doubler端点是这样的:

  1. /doubler/{args.num}

基于该端点,Space Cloud能够知道它要从GraphQL中获取一个参数num,并使用它作为变量来形成路径/doubler/50

成功调用之后,我们应该会看到如下所示的响应:

  1. {
  2. "doubler": {
  3. "result": 100
  4. }
  5. }

额外的奖励——服务链

如果成功遵循这个指南的话,我们会有一个应得的奖励!这个REST到GraphQL的转换为我们解锁了一个超级强大的功能:服务链(Service Chaining)。

让我们来看一个场景:

REST方式

如果我们在客户端代码中使用REST的话,上述任务将会如下所示:

请注意,我们从前端发出两个请求,就意味着往返时间会翻倍。它会导致较慢的响应时间和较差的用户体验。

GraphQL方式

现在,如果我们将客户端从REST切换为使用Space Cloud的GraphQL,那么我们的请求将会如下所示:

请注意,在这里,我们只从前端发起了一次GraphQL查询到后端(Space Cloud)。而Space Cloud发起了两次请求到REST服务器以满足该请求。但是,这些请求(从Space Cloud到我们的服务器)的往返时间是微不足道的,因为它们位于同一个网络中。

要完成上述任务,到Space Cloud的GraphQL查询如下所示:

  1. {
  2. adder(num1: 10, num2: 20) @arithmetic {
  3. doubler(num: "adder.result") @arithmetic {
  4. result
  5. }
  6. }
  7. }

请注意,我们是如何在得到adder服务的响应之后调用doubler服务的,而且我们将adder服务的result以参数的形式传递给doubler服务。

查询的结果将会如下所示:

  1. {
  2. "adder": {
  3. "doubler": {
  4. "result": 60
  5. }
  6. }
  7. }

我们可以猜到,我们得到的结果是60,即(10 + 20) * 2。

小提示:如果你想并行执行两个不相关的REST服务的话,我们可以在一个请求中完成,如下所示:

  1. {
  2. adder(num1: 10, num2: 20) @arithmetic {
  3. result
  4. }
  5. doubler(num: 50) @arithmetic {
  6. result
  7. }
  8. }

我把这个查询会得到什么响应作为作业留给读者。

结论

首先,鼓励一下你自己,因为你完整地读完了该指南。

通过该指南,我们学习到:

除了从REST迁移到GraphQL(如跨数据库连接)之外,我们还可以使用Space Cloud做更多的事情。如果你喜欢它的话,请在GitHub上为其添加一个star

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注