[譯]GraphQL:你需要知道的一切

楊濤1475815702752發表於2018-04-15

原文連結:https://medium.com/@weblab_tech/graphql-everything-you-need-to-know-58756ff253d8

原文作者:Weblab Technology

譯者:楊濤

[譯]GraphQL:你需要知道的一切
你可能已經構建和使用REST API一段時間了,並且最近開始聽說GraphQL--一種新型的API技術。有些人說它很好,但一些人並不認同。那麼,我相信你肯定很想知道GraphQL讓人驚奇的地方在哪和它與傳統方法有什麼不一樣。

這篇文章的目的是指出GraphQL相關的主要功能和討論特定API規範的優點和缺點。

GraphQL通常被描述為一種"前端導向"的API技術,因為它讓前端開發者以一種比以前簡單得多的方式請求資料。Facebook引入了這種查詢語言,它的目標是以符合直覺和可伸縮的方式定製客戶端應用,以描述資料的先決條件和互動。最好的一點是這種查詢語言不依賴於任何特定的資料庫管理系統,並且得到了當前資料格式和編碼方式的支援。

傳統REST的一個基本問題是,客戶端不能個性化的收集資料。除此之外,執行和控制多個端點(譯者注:表示API的具體網址,也可以理解為 介面)是另一個難點,因為客戶端經常需要從多個端點獲取資料。

當建立起一個GraphQL伺服器,只需要簡單的URL就能獲取和修改資料。因此,一個使用者可以通過傳遞查詢字串和宣告他們需要什麼來向伺服器請求資料集。

在我們繼續之前,在這你可以找到我們的個人實踐。 graphlql-example

GraphQL VS REST

[譯]GraphQL:你需要知道的一切

說到相似之處,REST和GraphQL都用於構建API。 另外,它們都可以通過HTTP進行管理。 至於差異,REST主要是一個以軟體為中心的結構化概念,沒有規範,也不求明確的工具集。它更關注API的穩定性而不是效能。 GraphQL,另一方面,是一種被設計於通過HTTP管理端點,提高效能和適用性的查詢語言。我甚至可以說,查詢語言和開發web的架構風格放在一起做比較,可能看起來很奇怪:)。一些其他顯著的不同包括:

資料獲取

毫無疑問,資料獲取是GraphQL的一個最引人矚目的特點之一。通過標準的REST API去生成和獲取資料,我們可能需要向多個端點發起請求。相比之下,GraphQL提供了可以獲取伺服器資料的單端點

query {
  books {
    id
    title
    author
    isbn
    price
  } 
}
複製程式碼

資料獲取之外

由於在REST中每個介面都包含固定的資料格式,相比GraphQL,它會讓你拿到更多的多餘資料。相似的,REST會傳送額外的請求去獲取相關資料。 對於上一個例子,GraphQL是很不一樣的。因為它是一種查詢語言並且支援宣告式獲取資料,使用者可以從伺服器只獲取他們需要的資料。

只查詢books的title和price
query {
 books {
   title
   price
 } 
}
複製程式碼

錯誤管理

在REST風格中,錯誤管理是非常簡單的。我們需要做的是檢查HTTP的headers以及瞭解response的位置。通過狀態碼,我們能快速的找到錯誤和合適的方式去解決它。另一方面,在GraphQL中,我們總是收到200 OK的狀態碼。

Request: query { books { error_field } }
Response:
Request Method:POST
Status Code: 200 OK
{“errors”:[{“message”:”Cannot query field \”error_field\” on type \”Book\”.”,”category”:”graphql”,”locations”:[{“line”:3,”column”:3}]}]}
複製程式碼

快取

因為REST強制使用具有快取機制的HTTP協議,你可以通過它避免獲取多餘資源。GraphQL,另一方面,沒有快取機制,它把快取的重任交給了使用者。

GraphQL的優點

[譯]GraphQL:你需要知道的一切

版本

資料控制會帶來API的邊界,任何的變動都會被視為一種破壞性的改變,而破壞性改變就需要更新API的版本。這也許就是大多數API選擇版本控制的原因。如果新API需要最新的版本,我們就需要頻繁在新API和原有API之間調整。[譯者注:例如介面改變了某個欄位的資料型別] 相比之下,GraphQL只返回我們需要的資料,在不改變原有的請求的情況下,拿到最新的資料型別和欄位。

棄用很容易

當使用GraphQL,你可以方便的棄用一個欄位。GraphQL使用者肯定會宣告他們需要的欄位。

‘author_name’ => [
  ‘type’ => Type::string(),
  ‘deprecationReason’ => ‘Deprecated. Use author field’,
 ],
複製程式碼

REST API 以不同的方式運作。雖然基本的端點都能在REST API中獲取,但不是所有端點都能返回稀疏欄位。[譯者注:即可能包含多餘的欄位] 相比之下,GraphQL非常容易監控特定欄位的使用。API使用者能在特定的客戶端部署獲取到的欄位。

效能優化

REST的請求預設作為一個整體,GraphQL通常儘量傳送最少的請求。即便REST的每個請求返回最基本的部分,相同情況下,GraphQL能傳輸更多的資料片段。

GraphQL的缺點

[譯]GraphQL:你需要知道的一切

GraphQL快取不容易

和預設採用能讓客戶端和代理端完美的工作的HTTP的REST不同,GraphQL以完全不同的方法呼叫。當然,事情並沒有像REST一樣簡單,因為你需要調整你的資料集,使用Redis的集合和總是需要祈禱客戶端能快取。

正如官方文件解釋的那樣, "在一個基於端點的API,客戶端能夠使用HTTP快取輕易的避免重複獲取資源和識別什麼時候兩個資源是一樣的。客戶端可以根據API中的URL作為全域性唯一的識別符號構建快取。在GraphQL中,沒有類似URL的物件能夠作為全域性唯一的識別符號。最佳實踐是提供這麼一個識別符號供客戶端使用"

鑑權問題

鑑權問題也是我們在使用GraphQL時關注的一個重要問題。將GraphQL作為一個特定領域語言考慮,它只是薄薄的一層放置在伺服器和客戶端中間。鑑權是單獨的一層,語言本身並不會對應用進行驗證和授權。但是你可以使用入口令牌(entry tokens)把客戶端和響應關聯起來。這與我們在REST中遵循的方法非常相似。

檢測和解決n+1問題

[譯]GraphQL:你需要知道的一切

什麼是n+1問題?

n+1問題是在做GraphQL後端時最明顯的可能遇到的優化問題。

如果你沒有優化你的GraphQL查詢,你可能在一次query進行多次查詢。沒有合適的快取和批量處理系統,每次確定欄位的時候伺服器都會響應一次請求。DataLoader無疑是最好的解決方案,可以極大地增強後端的效能,特別是在GraphQL伺服器中。 用一個簡單的例子描述n+1問題

query {
 users {
    name
    education {
      degree
      year
    }
    age
    address {
      country
      city
      street
    }
  }
}
複製程式碼

使用REST API是很容易評估,識別和解決n+1問題的。對於GraphQL有所不同。幸運的是,Facebook正在努力通過DataLoader解決這個問題

什麼是DataLoader

[譯]GraphQL:你需要知道的一切
DataLoader是一個用於使用者在GraphQL函式中讀取資料和訪問資料的基礎設施。 我們可以通過這個基礎設施直接從記錄中讀取資料而不是從SQL查詢。

它是怎麼執行的?

DataLoader主要使用了批處理和快取。它用於批量載入客戶端的多個 問題/請求的響應。此外,它可以快取響應和讓它們能響應連續類似的資源請求。

GraphQL中的Queries, Mutations, and Subscriptions

好的,我們已經強調了一些GraphQL重要的方面。但是要開發一個功能齊全的app,我們也需要知道一些其他用來增強功能和效能的部分。

Queries

正如它的名字,Queries是客戶端從服務端獲取資料。和從多個端點返回詳細資訊的REST不一樣,GraphQL只提供了一個端點,讓客戶端從預定義的框架決定它需要的資料。 例如:

{
 Users {
   name
 }
}
複製程式碼

上面的查詢提到的'Users'欄位稱為根欄位,其他資料稱為載荷。 這個查詢將會返回使用者名稱的列表:

{
  “Users”: [
  {“name”: “Damira”},
  {“name”: “Michael”}
  {“name”: “Salman”}
  {“name”: “Sara”}
  {“name”: “Maria”}
]
}
複製程式碼

值得注意的是,這個查詢只返回了使用者名稱(因為在我們的查詢中,我們只宣告瞭我們需要使用者名稱)。對於二外的請求,我們需要為它增加特定的細節。 例如,假設我們只希望獲取列表中的最後3個使用者的資訊。我們可以這麼寫引數來實現它。

{
  Users (last: 3) {
    name
    username
}}
複製程式碼

至此,我們已經看到如何通過查詢從伺服器獲取資料。現在讓我們看一下在GraphQL中建立,省略,更新資料的方法。

Mutations

Mutations用於建立,更新或者刪除資料。除了需要在開頭增加‘mutation’ 欄位,它的結構和queries幾乎一樣。例如,

mutation {
  createUser (name : “John”, username: ”jo123”){
    name
    username
  }
}
複製程式碼

Subscriptions

Subscriptions用於設定和儲存和伺服器的實時連線。它可以讓你獲取相關事件的實時資訊。大多數情況下,客戶端需要訂閱特定的事件來獲取相應的資料。 請通過graphql.org/learn/queri… 獲取詳細資訊

兩全其美的辦法

[譯]GraphQL:你需要知道的一切
儘管GraphQL解決了一些問題,它還是有著一些缺陷和不足。校驗,策略和快取只是其中的幾例。由於它本質上並不具備說服力,它並沒有指導使用者如何應用這些層。除此之外,在服務端和客戶端之間存在不具體的一層令人不安。

Aollo裡面的Stack 可以找到一部分問題的解決方案。

如果你有一個正在執行的專案,它是很難快速從RESTful API遷移到GraphQL的。但好訊息是你可以同時享受這兩種方法的好處。 例如你可以用GraphQL的queries去重構前端裡獲取資料的方式,然後再開始整合 mutations。它允許你緩慢的減少你的controllers裡的actions 此外,你可以在專案中長時間保持兩種方法並存。例如,如果你想簡化授權機制,你可以一直用REST架構提供幫助。

總結

[譯]GraphQL:你需要知道的一切
回憶起90年代末作為一個健壯的計算機網路應用互動結構化資料的協議,曾受到熱烈歡迎和獲得巨大的聲譽的SOAP。但是它的載荷明顯很高並且以前的應用容易提高的資料丟失和阻塞的機率。 REST是為了做到更好的利用web伺服器和提高適應性而引入的。這個概念非常的簡單直接,完全沒有狀態,因此可以放棄一些不相關的因素。除此之外,這種方法可以輕鬆的同時使用JSON和XML。但是,資料的統一是最大的障礙。此外版本號的分歧是另一個問題。為了解決這個問題,Facebook為開發者提供了一個兩全其美的方案-GraphQL。 GraphQL不是一個沒有具體實踐基礎的方案。RESTful已經在效率和表現方面證明了很多年。GraphQL能彌補REST的不足,REST可以填補GraphQL的空白。 值得注意的是,GraphQL和REST的情況和關係型資料庫和非關係型資料庫的關係很像。 使用GraphQL的時候,HTTP是服務端和客戶端通訊協議的最佳選擇,這主要是因為它的普遍性。然而,當使用HTTP2的時候,效能還是會有問題。 儘管GraphQL解決了一些問題,選擇任何一個API規範仍是困難的,因此你可以考慮讓兩者並存。 從設計到應用的整體功能,選擇一種API風格都會對整個API過程產生影響。因此,做選擇應當根據遠見的而不僅僅是基本的信仰。 這篇文章完全基於我們對這兩種方法的個人經驗。我們很希望能聽到你們關於GraphQL和RESTful API的看法。

一些有用的資料

https://github.com/weblab-technology/graphql-example

Implementing GraphQL as a Query Language for Deductive Databases in SWI-Prolog Using DCGs, Quasi Quotations, and Dicts

REST and Web Services — In Theory and in Practice — Springer

by Oleksandr Knyga, Software Engineer Maksim Kolesnikov, DevOps Sergei Guliaev, Back-End Developer Viacheslav Eremin, Front-End Developer Sharmeen Hayat, author & Data Specialist Dima Dmytriienko, editor & Brand Specialist

譯者注

原文提到的GraphQL一些問題,在Apollo GraphQL這個專案中都得到了一定程度的解決,比加快取。

相關文章