這個指南教你在不修改任何代碼的情況下,完成從REST到GraphQL的遷移。這樣,GraphQL就能讓你的REST真正休息一下了!
從REST到GraphQL
GraphQL的支持者在宣傳推廣GraphQL方面已經做得非常好了。出於對他們努力的尊重,我就不再深入介紹細節,只是稍微總結一下:
- GraphQL允許我們在單個請求中獲取多個資源;
- 通過精確描述所需數據,GraphQL解決了REST過度抓取數據的問題;
- 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架構最終如下圖所示:
我們的應用將會發起對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
我們需要爲自己的操作系統下載Space Cloud二進制文件,也可以通過其源碼直接進行構建。如果從源碼構建的話,需要go 1.13.0或更高版本。
可以通過以下鏈接下載對應操作系統的二進制文件:
下載後,我們解壓壓縮包。
對於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
第四步:下載Space Cloud
要以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
我們注意到,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設置爲:
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
即可。
第七步:通過GraphQL來查詢REST服務
現在,我們添加了REST和兩個端點到Space Cloud中,接下來,我們該使用統一的GraphQL API對其進行查詢。
跳轉至Explorer
區域:
嘗試在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的話,上述任務將會如下所示:
注意,我們從前端發出兩個請求,就意味着往返時間會翻倍。它會導致較慢的響應時間和較差的用戶體驗。
GraphQL方式
現在,如果我們將客戶端從REST切換爲使用Space Cloud的GraphQL,那麼我們的請求將會如下所示:
注意,在這裏,我們只從前端發起了一次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做更多事情。
相關閱讀: