为什么说 GraphQL 可以取代 REST API?
幾年前,我在 DocuSign 帶領了一個開發(fā)團隊,任務是重寫一個有數(shù)千萬個用戶在使用的 Web 應用程序。當時還沒有可以支持前端的 API,因為從一開始,Web 應用程序就是一個.NET 大單體。西雅圖的 API 團隊在將拆分單體,并逐步暴露出 RESTful API。這個 API 團隊由兩名工程師組成,發(fā)布周期為一個月,而我們在舊金山的前端團隊每周都會發(fā)布新版本。
API 團隊的發(fā)布周期太長,因為很多(幾乎所有)功能都必須進行手動測試,這是可以理解的。它畢竟是一個單體,而且沒有適當?shù)淖詣踊瘻y試——如果他們修改了一個地方,不知道在應用程序的其他地方會出現(xiàn)什么問題。
我記得有一次,我們的前端團隊面臨為某大會交付新版本的壓力,但我們忘記跟進一個重要的 API 變更,這個變更未被包含在即將發(fā)布的 API 版本中。我們要么一直等待,直到錯過截止日期,要么有人愿意放棄優(yōu)先權,以便讓我們的變更包括在即將發(fā)布的版本中。所幸的是,這個變更最后被包含在新版本中,我們也及時發(fā)布了新的前端版本。我真的希望當時我們已經(jīng)使用了 GraphQL,因為它可以消除對外部團隊及其發(fā)布周期的重度依賴。
在這篇文章中,我將介紹 GraphQL 的優(yōu)勢,以及為什么它會變得如此受歡迎。
很多公司已經(jīng)在內部從 RESTful 轉向了 GraphQL API:IBM、Twitter、Walmart Labs、紐約時報、Intuit、Coursera,等等。
其他一些公司不僅是在內部而且還將外部 API 也轉為 GraphQL:AWS、Yelp、GitHub、Facebook 和 Shopify,等等。GitHub 甚至打算停止使用 REST API,他們的 v4 版本只使用 GraphQL。
GraphQL 究竟是一個炒作流行語還是真正會帶來一場變革?有趣的是,我之前列出的大多數(shù)從 GraphQL 獲益的公司都有以下這些共同點。
-
他們擁有包括移動端在內的多個客戶端;
-
他們正在轉向或者已經(jīng)采用了微服務架構;
-
他們的遺留 REST API 數(shù)量暴增,變得十分復雜;
-
他們希望消除客戶端團隊對 API 團隊的依賴;
-
他們注重良好的 API 文檔和開發(fā)者體驗。
GitHub 工程團隊表明了他們的動機:
“GraphQL 彌合了發(fā)布的內容與可以使用的內容之間的差距。我們真的很期待能夠同時發(fā)布它們。GraphQL 代表了 API 開發(fā)的巨大飛躍。類型安全、內省、生成文檔和可預測的響應都為我們平臺的維護者和消費者帶來了好處。我們期待著由 GraphQL 提供支持的平臺進入新時代,也希望你們也這樣做!”
GraphQL 加速了開發(fā)速度,提升了開發(fā)者體驗,并提供了更好的工具。我并不是說這絕對是這樣的,但我會盡力說明 GraphQL 與 REST 之間的爭論點及其原因。
超級數(shù)據(jù)聚合器
我是 Indeed(世界排名第一的求職網(wǎng)站)的軟件工程負責人,所以讓我們先來看看 Indeed.com 的主頁和職位查詢結果頁面。它們分別發(fā)出了 10 和 11 個 XHR 請求。
需要注意的是,在 REST 中使用 POST 進行頁面瀏覽并不是很“正規(guī)”。
以下是其中的一些調用:
-
GET?https://inbox.indeed.com/api/getConversationCount
-
GET?https://www.indeed.com/rpc/jobdescs
-
GET?https://www.indeed.com/rpc/vjslog
-
GET?https://www.indeed.com/rpc/preccount
-
POST?https://www.indeed.com/rpc/jobalert
-
POST?https://www.indeed.com/rpc/count
在使用 GraphQL 時,上面的這些請求可以被包含在單個查詢和單個請求中。
復制代碼
| ? | query HomePage { |
| ? | ? getConversationCount(...) { |
| ? | ? ? ?... |
| ? | ? } |
| ? | ? jobdescs(...) { |
| ? | ? ? ?... |
| ? | ? } |
| ? | ? vjslog(...) { |
| ? | ? ? ?... |
| ? | ? } |
| ? | ? preccount(...) { |
| ? | ? ? ?… |
| ? | ? } |
| ? | ? jobalert(...) { |
| ? | ? ? ?… |
| ? | ? } |
| ? | ? count(...) { |
| ? | ? ? ?… |
| ? | ? } |
| ? | } |
響應結果可能是這樣的:
復制代碼
| ? | { |
| ? | ? "data": { |
| ? | ? ? "getConversationCount": [ |
| ? | ? ? ? { |
| ? | ? ? ? ? ... |
| ? | ? ? ? } |
| ? | ? ? ], |
| ? | ? ? "vjslog": [...], |
| ? | ? ? "preccount": [...], |
| ? | ? ? ? "jobalert": [...], |
| ? | ? ? "count": {} |
| ? | ? }, |
| ? | ? "errors": [] |
| ? | } |
通常,單個調用比多個調用更方便、更有效,因為它需要更少的代碼和更少的網(wǎng)絡開銷。來自 PayPal 過程團隊的開發(fā)體驗還證實,很多 UI 工作實際上不是 UI 工作,而是其他任務,例如前端和后端之間的通信:
“我們發(fā)現(xiàn),UI 開發(fā)人員實際用于構建 UI 的時間不到三分之一,剩下的時間用于確定在何處以及如何獲取數(shù)據(jù)、過濾 / 映射數(shù)據(jù)以及編排 API 調用,還有一些用于構建和部署。”
需要注意的是,有實時使多個請求也是有必要的,例如多個單獨的請求可以快速且異步獨立地獲取不同的數(shù)據(jù),如果采用了微服務架構,它們會增加部署靈活性,而且它們的故障點是多個,而不是一個。
此外,如果頁面是由多個團隊開發(fā)的,GraphQL 提供了一個功能,可以將查詢分解稱為片段。稍后我們將詳細介紹這方面的內容。
從更大的角度來看,GraphQL API 的主要應用場景是 API 網(wǎng)關,在客戶端和服務之間提供了一個抽象層。
微服務架構很好,但也存在一些問題,GraphQL 可以用來解決這些問題。以下是來自 IBM 在微服務架構中使用 GraphQL 的經(jīng)驗:
“總的來說,GraphQL 微服務的開發(fā)和部署都非常快。他們 5 月份開始開發(fā),7 月份就進入了生產環(huán)境。因為他們不需要征得許可,直接開干。他強烈推薦這個方案,比開會討論好太多了。”
接下來,讓我們逐一討論 GraphQL 的每一個好處。
提高開發(fā)速度
首先,GraphQL 有助于減少發(fā)出的請求數(shù)。通過單個調用來獲取所需的數(shù)據(jù)比使用多個請求要容易得多。從工程師的角度來看,這加快了開發(fā)速度。后面我會解釋更多有關為什么會提升開發(fā)速度的原因,但現(xiàn)在我想先說明另一個問題。
后端和客戶端團隊需要通過密切合作來定義 API、測試它們,并做出更改。前端、移動、物聯(lián)網(wǎng)(例如 Alexa)等客戶端團隊不斷迭代功能,并嘗試使用新的 UX 和設計。他們的數(shù)據(jù)需求經(jīng)常發(fā)生變化,后端團隊必須跟上他們的節(jié)奏。如果客戶端和后端代碼由同一團隊負責,那么問題就沒那么嚴重了。Indeed 的大多數(shù)工程團隊都是由全棧工程師組成,但并非全部都是這樣。對于非全棧團隊,客戶端團隊經(jīng)常因為依賴了后端團隊開發(fā)速度受到影響。
當我轉到 Job Seeker API 團隊時,移動團隊開始我們的開發(fā)進度。我們之間有很多關于參數(shù)、響應字段和測試的事情需要溝通。
在使用了 GraphQL 之后,客戶端工程師就可以完全控制前端,不需要依賴任何人,因為他們可以告訴后端他們需要什么以及響應結構應該是怎樣的。他們使用了 GraphQL 查詢,它們會告訴后端 API 應該要提供哪些數(shù)據(jù)。
客戶端工程師不需要花時間讓后端 API 團隊添加或修改某些內容。GraphQL 具有自文檔的特點,所以可以節(jié)省一些用于查找文檔以便了解如何使用 API 的時間。我相信大多數(shù)人曾經(jīng)在找出確切的請求參數(shù)方面浪費了很多時間。GraphQL 協(xié)議本身及其社區(qū)在文檔方面為我們提供了一些有用的工具。在某些情況下,可以從模式自動生成文檔。其他時候,只需使用 GraphiQL Web 界面就足以編寫一個查詢。
來自紐約時報的工程師表示,他們在轉到 GraphQL 和 Relay 之后,在做出變更時不需要改太多的東西:
“當我們想要更新所有產品的設計時,不再需要修改多個代碼庫。這就是我們想要的。我們認為 Relay 和 GraphQL 是幫助我們實現(xiàn)這個偉大目標的完美工具。”
當一家公司已經(jīng)擁有大量 GraphQL API,然后有人想出了一個新的產品創(chuàng)意,這也是我最喜歡 GraphQL 的應用場景。使用已有的 GraphQL API 實現(xiàn)原型比調用各種 REST 端點(將提供太少或太多的數(shù)據(jù))或為新應用程序構建新的 REST API 要快得多。
開發(fā)速度的提升與開發(fā)者體驗的提升密切相關。
提升開發(fā)者體驗
GraphQL 提供了更好的開發(fā)者體驗(DX),開發(fā)者將花更少的時間思考如何獲取數(shù)據(jù)。在使用 Apollo 時,他們只需要在 UI 中聲明數(shù)據(jù)。數(shù)據(jù)和 UI 放在一起,閱讀代碼和編寫代碼都變得更方便。
通常,在開發(fā) UI 時需要在 UI 模板、客戶端代碼和 UI 樣式之間跳轉。GraphQL 允許工程師在客戶端開發(fā) UI,減少摩擦,因為工程師在添加或修改代碼時無需在文件之間切換。如果你熟悉 React,這里有一個很好的比喻:GraphQL 之于數(shù)據(jù),就像 React 之于 UI。
下面是一個簡單的示例,UI 中直接包含了屬性名稱launch.name和?launch.rocket.name?。
復制代碼
| ? | const GET_LAUNCHES = gql` |
| ? | query launchList($after: String) { |
| ? | launches(after: $after) { |
| ? | launches { |
| ? | id |
| ? | name |
| ? | isBooked |
| ? | rocket { |
| ? | id |
| ? | name |
| ? | } |
| ? | } |
| ? | } |
| ? | } |
| ? | `; |
| ? | ? |
| ? | export default function Launches() { |
| ? | return ( |
| ? | <Query query={GET_LAUNCHES}> |
| ? | {({ data, loading, error }) => { |
| ? | if (loading) return <Loading />; |
| ? | if (error) return <p>ERROR</p>; |
| ? | ? |
| ? | return ( |
| ? | <div> |
| ? | {data.launches.launches.map(launch => ( |
| ? | <div |
| ? | key={launch.id} |
| ? | >{launch.name}<br/> |
| ? | Rocket: {launch.rocket.name} |
| ? | </div> |
| ? | ))} |
| ? | </div> |
| ? | ); |
| ? | }} |
| ? | </Query> |
| ? | ); |
| ? | }; |
使用這種方法,可以非常容易地修改或向 UI 或查詢(gql)添加新字段。React 組件的可移植性更強了,因為它們描述了所需的所有數(shù)據(jù)。
如前所述, GraphQL 提供了更好的文檔,而且還有一個叫作 GraphiQL 的 IDE:
前端工程師很喜歡 GraphiQL,下面引用 Indeed 的一位高級工程師說過的話:
“我認為開發(fā)體驗中最好的部分是能夠使用 GraphiQL。對我來說,與典型的 API 文檔相比,這是一種編寫查詢更有效的輔助方法”。
GraphQL 的另一個很棒的功能是片段,因為它允許我們在更高的組件層面重用查詢。
這些功能改善了開發(fā)者體驗,讓開發(fā)人員更快樂,更不容易出現(xiàn) JavaScript 疲勞。
提升性能
工程師并不是唯一從 GraphQL 中受益的人。用戶也會從中受益,因為應用程序的性能獲得了提升(可以感知到的):
1. 減少了有效載荷(客戶端只需要必要的東西);
2. 多個請求合并為一個請求可減少網(wǎng)絡開銷;
3. 使用工具可以更輕松地實現(xiàn)客戶端緩存和后端批處理和后端緩存;
4. 預取;
5. 更快的 UI 更新。
PayPal 使用 GraphQL 重新設計了他們的結賬流程。下面是來自用戶的反饋:
“REST 的原則并沒有為 Web 和移動應用及其用戶的需求考慮,這個在結賬優(yōu)化交易中體現(xiàn)得尤為明顯。用戶希望能夠盡快完成結賬,如果應用程序使用了很多原子 REST API,就需要在客戶端和服務器之間進行多次往返以獲取數(shù)據(jù)。我們的結賬每次往返網(wǎng)絡時間至少需要 700 毫秒,這還不包括服務器處理請求的時間。每次往返都會導致渲染變慢,用戶體驗不好,結算轉換率也會降低。”
性能改進中有一項是“多個請求組合成一個請求可以減少網(wǎng)絡開銷”。對于 HTTP/1 而言,這是非常正確的,因為它沒有 HTTP/2 那樣的多路復用機制。但盡管 HTTP/2 提供的多路復用機制有助于優(yōu)化單獨的請求,但它對于圖遍歷(獲取相關或嵌套對象)并沒有實際幫助。讓我們來看一看 REST 和 GraphQL 是如何處理嵌套對象和其他復雜請求的。
標準化和簡化復雜的 API
通常,客戶端會發(fā)出復雜的請求來獲取有序、排好序、被過濾過的數(shù)據(jù)或子集(用于分頁),或者請求嵌套對象。GraphQL 支持嵌套數(shù)據(jù)和其他難以使用標準 REST API 資源(也叫端點或路由)實現(xiàn)的查詢。
例如,我們假設有三種資源:用戶、訂閱和簡歷。工程師需要按順序進行兩次單獨的調用(這會降低性能)來獲取一個用戶簡歷,首先需要通過調用獲取用戶資源,拿到簡歷 ID,然后再使用簡歷 ID 來獲取簡歷數(shù)據(jù)。對于訂閱來說也是一樣的。
1.GET /users/123:響應中包含了簡歷 ID 和工作崗位通知訂閱的 ID 清單;
2.GET /resumes/ABC:響應中包含了簡歷文本——依賴第一個請求;
3.GET /subscriptions/XYZ:響應中包含了工作崗位通知的內容和地址——依賴第一個請求。
上面的示例很糟糕,原因有很多:客戶端可能會獲得太多數(shù)據(jù),并且必須等待相關的請求完成了以后才能繼續(xù)。此外,客戶端需要實現(xiàn)如何獲取子資源(例如建立或訂閱)和過濾。
想象一下,一個客戶端可能只需要第一個訂閱的內容和地址以及簡歷中的當前職位,另一個客戶端可能需要所有訂閱和整個簡歷列表。所以,如果使用 REST API,對第一個客戶端來說有點不劃算。
另一個例子:用戶表里可能會有用戶的名字和姓氏、電子郵件、簡歷、地址、電話、社會保障號、密碼(當然是經(jīng)過混淆的)和其他私人信息。并非每個客戶端都需要所有字段,有些應用程序可能只需要用戶電子郵件,所以向這些應用程序發(fā)送社會保障號等信息就不太安全。
當然,為每個客戶端創(chuàng)建不同的端點也是不可行的,例如 /api/v1/users 和 /api/v1/usersMobile。事實上,各種客戶端通常都有不同的數(shù)據(jù)需求:/api/v1/userPublic、/api/v1/userByName、/api/v1/usersForAdmin,如果這樣的話,端點會呈指數(shù)級增長。
GraphQL 允許客戶要求 API 發(fā)送他們想要的字段,這將使后端工作變得更加容易:/api/gql——所有客戶端只需要這個端點。
注意:對于 REST 和 GraphQL,后端都需要使用訪問控制級別。
或者可以使用舊 REST 來實現(xiàn) GraphQL 的很多功能。但是這樣要付出什么代價?后端可以支持復雜的 RESTful 請求,這樣客戶端就可以使用字段和嵌套對象進行調用:
復制代碼
| ? | GET /users/?fields=name,address&include=resumes,subscriptions |
上面的請求將比使用多個 REST 請求更好,但它不是標準化的,不受客戶端庫支持,而且這樣的代碼也更難編寫和維護。對于相對復雜的 API,工程師需要在查詢中使用自己的查詢字符串參數(shù)約定,最終得到類似 GraphQL 的東西。既然 GraphQL 已經(jīng)提供了標準和庫,為什么還要基于 REST 設計自己的查詢約定呢?
將復雜的 REST 端點與以下的 GraphQL 嵌套查詢進行對比,嵌套查詢使用了更多的過濾條件,例如“只要給我前 X 個對象”和“按時間按升序排列”(可以添加無限制的過濾選項):
復制代碼
| ? | { |
| ? | user (id: 123) { |
| ? | id |
| ? | firstName |
| ? | lastName |
| ? | address { |
| ? | city |
| ? | country |
| ? | zip |
| ? | } |
| ? | resumes (first: 1, orderBy: time_ASC) { |
| ? | text |
| ? | title |
| ? | blob |
| ? | time |
| ? | } |
| ? | subscriptions(first: 10) { |
| ? | what |
| ? | where |
| ? | time |
| ? | } |
| ? | } |
| ? | } |
| ? | } |
在使用 GraphQL 時,我們可以在查詢中保留嵌套對象,對于每個對象,我們將精確地獲得我們需要的數(shù)據(jù),不多也不少。
響應消息的數(shù)據(jù)格式反映了請求查詢的結構,如下所示:
復制代碼
| ? | { |
| ? | "data": { |
| ? | "user": { |
| ? | "id": 123, |
| ? | "firstName": "Azat", |
| ? | "lastName": "Mardan", |
| ? | "address": { |
| ? | "city": "San Francisco", |
| ? | "country": "US", |
| ? | "zip": "94105" |
| ? | }, |
| ? | "resumes" [ |
| ? | { |
| ? | "text": "some text here...", |
| ? | "title": "My Resume", |
| ? | "blob": "<BLOB>", |
| ? | "time": "2018-11-13T21:23:16.000Z" |
| ? | }, |
| ? | ], |
| ? | "subscriptions": [ ] |
| ? | }, |
| ? | "errors": [] |
| ? | } |
相比復雜的 REST 端點,使用 GraphQL 的另一個好處是提高了安全性。這是因為 URL 經(jīng)常會被記錄下來,而 RESTful GET 端點依賴于查詢字符串(是 URL 的一部分)。這可能會暴露敏感數(shù)據(jù),所以 RESTful GET 請求的安全性低于 GraphQL 的 POST 請求。我打賭這就是為什么 Indeed 主頁會使用 POST 發(fā)出“閱讀”頁面請求。
使用 GraphQL 可有更容易地實現(xiàn)分頁等關鍵功能,這要歸功于查詢以及 BaaS 提供商提供的標準,以及后端的實現(xiàn)和客戶端庫使用的標準。
改進的安全性、強類型和驗證
GraphQL 的 schema 與語言無關。對前面的示例進行擴展,我們可以在 schema 中定義 Address 類型:
復制代碼
| ? | type Address { |
| ? | city: String! |
| ? | country: String! |
| ? | zip: Int |
| ? | } |
String 和 Int 是標量類型,! 表示字段不可為空。
schema 驗證是 GraphQL 規(guī)范的一部分,因此像這樣的查詢將返回錯誤,因為 name 和 phone 不是 Address 對象的字段:
復制代碼
| ? | { |
| ? | user (id: 123) { |
| ? | address { |
| ? | name |
| ? | phone |
| ? | } |
| ? | } |
| ? | } |
我們可以使用我們的類型構建復雜的 GraphQL schema。例如,用戶類型可能會使用我們的地址、簡歷和訂閱類型,如下所示:
復制代碼
| ? | type User { |
| ? | id: ID! |
| ? | firstName: String! |
| ? | lastName: String! |
| ? | address: Address! |
| ? | resumes: [Resume] |
| ? | subscriptions: [Subscription] |
| ? | } |
Indeed 的大量對象和類型都是使用 ProtoBuf 定義的。類型化數(shù)據(jù)并不是什么新鮮事物,而且類型數(shù)據(jù)的好處也是眾所周知。與發(fā)明新的 JSON 類型標準相比,GraphQL 的優(yōu)點在于已經(jīng)存在可以從 ProtoBuf 自動換換到 GraphQL 的庫。即使其中一個庫(rejoiner)不能用,也可以開發(fā)自己的轉換器。
GraphQL 提供了比 JSON RESTful API 更強的安全性,主要有兩個原因:強類型 schema(例如數(shù)據(jù)驗證和無 SQL 注入)以及精確定義客戶端所需數(shù)據(jù)的能力(不會無意泄漏數(shù)據(jù))。
靜態(tài)驗證是另一個優(yōu)勢,可以幫助工程師節(jié)省時間,并在進行重構時提升工程師的信心。諸如eslint-plugin-graphql之類的工具可以讓工程師知道后端發(fā)生的變化,并讓后端工程師確保不會破壞客戶端代碼。
保持前端和后端之間的契約是非常重要的。在使用 REST API 時,我們要小心不要破壞了客戶端代碼,因為客戶端無法控制響應消息。相反,GraphQL 為客戶端提供了控制,GraphQL 可以頻繁更新,而不會因為引入了新類型造成重大變更。因為使用了 schema,所以 GraphQL 是一種無版本的 API。
GraphQL 的實現(xiàn)
在選擇實現(xiàn) GraphQL API 的平臺時,Node 是一個候選項,因為最初 GraphQL 用于 Web 應用程序和前端,而 Node 是開發(fā) Web 應用程序的首選,因為它是基于 JavaScript 的。使用 Node 可以非常容易地實現(xiàn) GraphQL(假設提供了 schema)。事實上,使用 Express 或 Koa 來實現(xiàn)只需要幾行代碼:
復制代碼
| ? | const Koa = require('koa'); |
| ? | const Router = require('koa-router'); // koa-router@7.x |
| ? | const graphqlHTTP = require('koa-graphql'); |
| ? | ? |
| ? | const app = new Koa(); |
| ? | const router = new Router(); |
| ? | ? |
| ? | router.all('/graphql', graphqlHTTP({ |
| ? | schema: schema, |
| ? | graphiql: true |
| ? | })); |
| ? | ? |
| ? | app.use(router.routes()).use(router.allowedMethods()); |
schema 是使用 npm 的 graphql 中的類型來定義的。Query 和 Mutation 是特殊的 schema 類型。
GraphQL API 的大部分實現(xiàn)都在于 schema 和解析器。解析器可以包含任意代碼,但最常見的是以下五個主要類別:
-
調用 Thrift、gRPC 或其他 RPC 服務;
-
調用 HTTP REST API(當優(yōu)先事項不是重寫現(xiàn)有 REST API 時);
-
直接調用數(shù)據(jù)存儲;
-
調用其他 GraphQL schema 查詢或服務;
-
調用外部 API。
這里有一個示例。
Node 很棒,但在 Indeed,我們主要使用 Java。包括 Java 在內的很多語言都支持 GraphQL,例如https://github.com/graphql-go和https://github.com/graphql-python。
由于 Indeed 主要使用了 Java,因此這里給出一個使用 graphql-java 的 Java GraphQL 示例,完整代碼位于這里。它定義了 /graphql 端點:
復制代碼
| ? | import com.coxautodev.graphql.tools.SchemaParser; |
| ? | import javax.servlet.annotation.WebServlet; |
| ? | import graphql.servlet.SimpleGraphQLServlet; |
| ? | ? |
| ? | @WebServlet(urlPatterns = "/graphql") |
| ? | public class GraphQLEndpoint extends SimpleGraphQLServlet { |
| ? | ? |
| ? | public GraphQLEndpoint() { |
| ? | super(SchemaParser.newParser() |
| ? | .file("schema.graphqls") //parse the schema file created earlier |
| ? | .build() |
| ? | .makeExecutableSchema()); |
| ? | } |
| ? | } |
GraphQL 的 schema 使用 POJO 來定義。GraphQL 端點類使用了 LinkRepository POJO。解析器包含了操作的(例如獲取鏈接)實際代碼:
復制代碼
| ? | @WebServlet(urlPatterns = "/graphql") |
| ? | public class GraphQLEndpoint extends SimpleGraphQLServlet { |
| ? | ? |
| ? | public GraphQLEndpoint() { |
| ? | super(buildSchema()); |
| ? | } |
| ? | ? |
| ? | private static GraphQLSchema buildSchema() { |
| ? | LinkRepository linkRepository = new LinkRepository(); |
| ? | return SchemaParser.newParser() |
| ? | .file("schema.graphqls") |
| ? | .resolvers(new Query(linkRepository)) |
| ? | .build() |
| ? | .makeExecutableSchema(); |
| ? | } |
| ? | } |
在很多情況下,GraphQL 的 schema 可以從其他類型的 schema 自動生成,例如 gRPC、Boxcar、ProtoBuf 或 ORM/ODM。
GraphQL 不一定需要客戶端。一個簡單的 GraphQL 請求就是一個常規(guī)的 POST HTTP 請求,其中包含了查詢內容。我們可以使用任意的 HTTP 代理庫(如 CURL、axios、fetch、superagent 等)來生成請求。例如,在終端中使用 curl 發(fā)送請求:
復制代碼
| ? | curl \ |
| ? | -X POST \ |
| ? | -H "Content-Type: application/json" \ |
| ? | --data '{ "query": "{ posts { title } }" }' \ |
| ? | https://1jzxrj179.lp.gql.zone/graphql |
| ? | ? |
以下代碼可以在任意一個現(xiàn)代瀏覽器(為了避免 CORS,請訪問 launchpad.graphql.com)中運行。
復制代碼
| ? | fetch('https://1jzxrj179.lp.gql.zone/graphql', { |
| ? | method: 'POST', |
| ? | headers: { 'Content-Type': 'application/json' }, |
| ? | body: JSON.stringify({ query: '{ posts { title } }' }), |
| ? | }) |
| ? | .then(res => res.json()) |
| ? | .then(res => console.log(res.data)); |
雖然構建 GraphQL 請求很容易,但是還需要實現(xiàn)很多其他東西,比如緩存,因為緩存可以極大地改善用戶體驗。構建客戶端緩存不是那么容易,所幸的是,Apollo 和 Relay Modern 等提供了開箱即用的客戶端緩存。
什么時候不該使用 GraphQL?
當然,完美的解決方案是不存在的(盡管 GraphQL 接近完美),還有一些問題需要注意,例如:
1. 它有單點故障嗎?
2. 它可以擴展嗎?
3. 誰在使用 GraphQL?
最后,以下列出了我們自己的有關 GraphQL 可能不是一個好選擇的主要原因:
-
當客戶端的需求很簡單時:如果你的 API 很簡單,例如 /users/resumes/123,那么 GraphQL 就顯得有點重了;
-
為了加快加載速度使用了異步資源加載;
-
在開發(fā)新產品時使用新的 API,而不是基于已有的 API;
-
不打算向公眾公開 API;
-
不需要更改 UI 和其他客戶端;
-
產品開發(fā)不活躍;
-
使用了其他一些 JSON schema 或序列化格式。
總結
GraphQL 是一種協(xié)議和一種查詢語言。GraphQL API 可以直接訪問數(shù)據(jù)存儲,但在大多數(shù)情況下,GraphQL API 是一個數(shù)據(jù)聚合器和一個抽象層,一個可以提升開發(fā)速度、減少維護工作并讓開發(fā)人員更快樂的層。因此,GraphQL 比公共 API 更有意義。很多公司開始采用 GraphQL。IBM、PayPal 和 GitHub 聲稱在使用 GraphQL 方面取得了巨大的成功。如果 GraphQL 很有前途,我們現(xiàn)在是否可以停止構建過時且笨重的 REST API,并擁抱 GraphQL?
英文原文:https://webapplog.com/graphql/
總結
以上是生活随笔為你收集整理的为什么说 GraphQL 可以取代 REST API?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 最小二乘与最大似然估计之间的关系
- 下一篇: Hystrix熔断原理