Categories
程式開發

分分鐘將REST轉換為GraphQL


這個指南教你在不修改任何代碼的情況下,完成從REST到GraphQL的遷移。這樣,GraphQL就能讓你的REST真正休息一下了!

從REST到GraphQL

GraphQL的支持者在宣傳推廣GraphQL方面已經做得非常好了。出於對他們努力的尊重,我就不再深入介紹細節,只是稍微總結一下:

  1. GraphQL允許我們在單個請求中獲取多個資源;
  2. 通過精確描述所需數據,GraphQL解決了REST過度抓取數據的問題;
  3. GraphQL借助在一個請求中獲取相關的數據,解決了前端N+1查詢的問題。

本指南中,我會介紹大多數倡導GraphQL的人所忽略的一個點,就是“我們在REST上有了巨大投入”。這意味著:

  • 我們大多數的服務都是基於REST的;
  • 我們更願意編寫REST服務;
  • 我們希望支持使用REST API的現有客戶端。

雖然有許多文章幫我們從REST遷移到GraphQL,但這些做法都迫使我們更改現有的代碼庫或在REST服務前面編寫新的代碼庫。

但是,稍等一下……

如果它還能運行,就別去碰它。

這難道不是編程的第一規則嗎?

遷移可能非常痛苦,尤其是面對巨大的代碼庫時,更會令人望而生畏。隨時存在將已有功能破壞掉的可能性。

我們為什麼不能繼續保持REST呢?我們天性懶惰,都喜歡用簡單的技巧和容易的解決方案。

如果有一種方式,讓我們原樣保持REST服務,且無需任何代碼變更就能在其之上實現一個GraphQL層,那會怎樣?聽起來很神奇,Space Cloud就能幫我們實現這一切

Space Cloud是什麼?

簡而言之,Space Cloud是一個開源的Web服務器,它能在數據庫和微服務之上即時提供GraphQL和REST API。

Space Cloud最酷的一點在於,所有API都是實時的。我們可以選擇訂閱數據庫的變更。在實現實時應用時,該功能非常便利。

本指南中,我們會用Space Cloud的remote service模塊將REST服務遷移成GraphQL。

架構

基於REST之上的GraphQL架構最終如下圖所示:

分分鐘將REST轉換為GraphQL 1

我們的應用將會發起對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中接收到兩個數字,即num1num2,接下來所做的就是返回這兩個數字的和。
  • 翻倍計算端點:我們將通過URL路徑參數得到的數字乘以2,然後返回。

對於服務來講,我們做這些就足夠了。

第二步:啟動服務

要運行服務,我們只需執行如下命令即可:

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

它將會展現如下輸出:

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啟動時,我們會看到如下所示的輸出:

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

我們注意到,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按鈕,打開如下界面:

分分鐘將REST轉換為GraphQL 2

為你的項目設置一個name

在這裡選擇什麼數據庫無關緊要,因為我們不會用到它。

點擊Next創建項目。

第六步:添加遠程服務到Space Cloud中

導航至Mission Control的Remote Services區域。

點擊Add first remote service按鈕來打開如下的表單:

分分鐘將REST轉換為GraphQL 3

將服務名設置為arithmetic,並將服務的URL設置為:

http://localhost:5000

添加完遠程服務之後,在遠程服務的表格中,我們應該就能看到它:

分分鐘將REST轉換為GraphQL 4

點擊Actions列中的Add按鈕,將會打開服務頁面。

點擊Add first remote endpoint按鈕,打開如下所示表單:

分分鐘將REST轉換為GraphQL 5

為求和計算端點添加如下內容:

  • Name:adder
  • Method:POST
  • Path:/adder

再次點擊“Add”按鈕並添加doubler端點:

  • Name:doubler
  • Method:GET
  • Path:/doubler/{args.num}

注意:現在不要擔心{args.num}部分,只需要確保將Method設置為GET即可。

第七步:通過GraphQL來查詢REST服務

現在,我們添加了REST和兩個端點到Space Cloud中,接下來,我們該使用統一的GraphQL API對其進行查詢。

跳轉至Explorer區域:

分分鐘將REST轉換為GraphQL 6

嘗試在GraphiQL explorer中運行如下的GraphQL查詢:

{
  adder(num1: 10, num2: 20) @arithmetic {
    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) @arithmetic {
    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的話,上述任務將會如下所示:

分分鐘將REST轉換為GraphQL 7

注意,我們從前端發出兩個請求,就意味著往返時間會翻倍。它會導致較慢的響應時間和較差的用戶體驗。

GraphQL方式

現在,如果我們將客戶端從REST切換為使用Space Cloud的GraphQL,那麼我們的請求將會如下所示:

分分鐘將REST轉換為GraphQL 8

注意,在這裡,我們只從前端發起了一次GraphQL查詢到後端(Space Cloud)。而Space Cloud發起兩次請求到REST服務器以滿足該請求。但是,這些請求(從Space Cloud到我們的服務器)的往返時間是微不足道的,因為它們位於同一個網絡中。

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

{
  adder(num1: 10, num2: 20) @arithmetic {
    doubler(num: "adder.result") @arithmetic {
      result
    }
  }
}

請注意,我們是如何在得到adder服務的響應後調用doubler服務的,而且我們將adder服務的result以參數形式傳遞給doubler服務。

查詢結果將會如下所示:

{
  "adder": {
    "doubler": {
      "result": 60
    }
  }
}

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

小提示:如果你想並行執行兩個不相關REST服務的話,我們可以在一個請求中完成,如下所示:

{
  adder(num1: 10, num2: 20) @arithmetic {
    result
  }
  doubler(num: 50) @arithmetic {
    result
  }
}

我把這個查詢會得到什麼響應作為作業留給讀者。

結論

首先,鼓勵一下自己,因為你完整地讀完了該指南。

通過該指南,我們學到:

  • 從REST遷移到GraphQL不需要更改代碼。
  • 我們不需要在REST和GraphQL之間進行非此即彼的選擇。我們可以在同一個應用程序中同時支持REST和GraphQL。
  • 借助Space Cloud來使用GraphQL會帶來網絡方面的收益,有助於減少往返時間。

除了從REST遷移到GraphQL(如跨數據庫連接)之外,我們還可以用Space Cloud做更多事情。

相關閱讀:

https://dzone.com/articles/rest-to-graphql-in-minutes