[譯] 將現有的 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前端後端產品設計 等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃

相關文章
相關標籤/搜索