是什麼讓我放棄了 restful api?瞭解清楚後我全面擁抱 GraphQL
背景
REST 作爲一種現代網絡應用非常流行的軟件架構風格,自從 Roy Fielding 博士在 2000 年他的博士論文中提出來到現在已經有了 20 年的歷史。它的簡單易用性,可擴展性,伸縮性受到廣大 Web 開發者的喜愛。
REST 的 API 配合 JSON 格式的數據交換,使得前後端分離、數據交互變得非常容易,而且也已經成爲了目前 Web 領域最受歡迎的軟件架構設計模式。
但隨着 REST API 的流行和發展,它的缺點也暴露了出來:
- 濫用 REST 接口,導致大量相似度很高(具有重複性)的 API 越來越冗餘。
- 對於前端而言:REST API 粒度較粗,難以一次性符合前端的數據要求,前端需要分多次請求接口數據。增加了前端人員的工作量。
- 對於後端而言:前端需要的數據往往在不同的地方具有相似性,但卻又不同,比如針對同樣的用戶信息,有的地方只需要用戶簡要信息(比如頭像、暱稱),有些地方需要詳細的信息,這就需要開發不同的接口來滿足這些需求。當這樣的相似但又不同的地方多的時候,就需要開發更多的接口來滿足前端的需要。增加了後端開發人員的工作量和重複度。
那我們來分析一下,當前端需求變化,涉及到改動舊需求時,會有以下這些情況:
做加法:
產品需求增加,頁面需要增加功能,數據也就相應的要增加顯示,那麼 REST 接口也需要做增加,這種無可厚非。
做減法:
產品需求減少,頁面需要減少功能,或者減少某些信息顯示,那麼數據就要做減法。
一種通常懶惰的做法是,前端不與後端溝通,僅在前端對數據選擇性顯示。
因爲後端接口能夠滿足數據需要,僅僅是在做顯示的時候對數據進行了選擇性顯示,但接口的數據是存在冗餘的,這種情況一個是存在數據泄露風險,另外就是數據量過大時造成網絡流量過大,頁面加載緩慢,用戶流量費白白消耗,用戶體驗就會下降。
另外一種做法就是告知後端,要麼開發新的接口,要麼,修改舊接口,刪掉冗餘字段。
但一般來說,開發新接口往往是後端開發人員會選擇的方案,因爲這個方案對現有系統的影響最低,不會有額外的風險。
修改舊接口刪除冗餘數據的方案往往開發人員不會選擇,這是爲什麼呢?
這就涉及到了系統的穩定性問題了,舊接口往往不止是一個地方在用,很有可能很多頁面、設置不同客戶端、不同服務都調用了這個接口獲取數據,不做詳細的調查,是不可能知道到底舊接口被調用了多少次,一旦改動舊接口,涉及範圍可能非常大,往往會引起其他地方出現崩潰。改動舊接口成本太高,所以往往不會被採取。
同時做加減法:
既有加法,又有減法,其實這種就跟新需求沒啥區別,前端需要重做頁面,後端需要新寫接口滿足前端需要,但是舊接口還是不能輕舉妄動(除非確定只有這一處調用纔可以刪除)。
往往這個時候,其實用到的數據大多都是來自於同一個 DO 或者 DTO,不過是在 REST 接口組裝數據時,用不同的 VO 來封裝不同字段,或者,使用同樣的 VO,組裝數據時做刪減。
看到這些問題是不是覺得令人頭大?
所以需求頻繁改動是萬惡之源,當產品小哥哥改動需求時,程序員小哥哥可能正提着鐵鍬趕來......
那麼有沒有一種方案或者框架,可以使得在用到同一個領域模型(DO 或者 DTO)的數據時,前端對於這個模型的數據字段需求的改動,後端可以根據前端的改動和需要,自動適配,自動組裝需要的字段,返回給前端呢?如果能這樣做的話,那麼後端程序猿小哥可能要開心死了,前端妹子也不用那麼苦口婆心地勸說後端小哥哥了。
所以 GraphQL 隆重出世了!那麼問題來了!
Part 1 What is GraphQL
GraphQL 簡介
- GraphQL 是一種新的 API 標準,它提供了一種比 REST 更有效、更強大和更靈活的替代方案。
- 它是由 Facebook 開發並開源的,現在由來自世界各地的公司和個人組成的大型社區維護。
- GraphQL 本質上是一種基於 api 的查詢語言,現在大多數應用程序都需要從服務器中獲取數據,這些數據存儲可能存儲在數據庫中,API 的職責是提供與應用程序需求相匹配的存儲數據的接口。
- 它是數據庫無關的,而且可以在使用 API 的任何環境中有效使用,我們可以理解爲 GraphQL 是基於 API 之上的一層封裝,目的是爲了更好,更靈活的適用於業務的需求變化。
簡單的來說,它
它的工作模式是這樣子的:
GraphQL 對比 REST API 有什麼好處?
REST API 的接口靈活性差、接口操作流程繁瑣,GraphQL 的聲明式數據獲取,使得接口數據精確返回,數據查詢流程簡潔,照顧了客戶端的靈活性。
客戶端拓展功能時要不斷編寫新接口(依賴於服務端),GraphQL 中一個服務僅暴露一個 GraphQL 層,消除了服務器對數據格式的硬性規定,客戶端按需請求數據,可進行單獨維護和改進。
REST API 基於 HTTP 協議,不能靈活選擇網絡協議,而傳輸層無關、數據庫技術無關使得 GraphQL 有更加靈活的技術棧選擇,能夠實現在網絡協議層面優化應用。
舉個經典的例子:前端向後端請求一個 book 對象的數據及其作者信息。
我用動圖來分別演示下 REST 和 GraphQL 是怎麼樣的一個過程。
先看 REST API 的做法:
REST API 獲取數據
再來看 GraphQL 是怎麼做的:
GraphQL 獲取數據
可以看出其中的區別:
- 與 REST 多個 endpoint 不同,每一個的 GraphQL 服務其實對外只提供了一個用於調用內部接口的端點,所有的請求都訪問這個暴露出來的唯一端點。
Endpoints 對比
REST API's Endpoints
- GraphQL 實際上將多個 HTTP 請求聚合成了一個請求,將多個 restful 請求的資源變成了一個從根資源 POST 訪問其他資源的 Comment 和 Author 的圖,多個請求變成了一個請求的不同字段,從原有的分散式請求變成了集中式的請求,因此 GraphQL 又可以被看成是圖數據庫的形式。
圖數據庫模式的數據查詢
那我們已經能看到 GraphQL 的先進性,接下來看看它是怎麼做的。
GraphQL 思考模式
使用 GraphQL 接口設計獲取數據需要三步:
GraphQL 獲取數據三步驟
- 首先要設計數據模型,用來描述數據對象,它的作用可以看做是 VO,用於告知 GraphQL 如何來描述定義的數據,爲下一步查詢返回做準備;
- 前端使用模式查詢語言(Schema)來描述需要請求的數據對象類型和具體需要的字段(稱之爲聲明式數據獲取);
- 後端 GraphQL 通過前端傳過來的請求,根據需要,自動組裝數據字段,返回給前端。
GraphQL 的這種思考模式是不是完美解決了之前遇到的問題呢?!
總結它的好處:
在它的設計思想中,GraphQL 以圖的形式將整個 Web 服務中的資源展示出來,客戶端可以按照其需求自行調用,類似添加字段的需求其實就不再需要後端多次修改了。
創建 GraphQL 服務器的最終目標是:
允許查詢通過圖和節點的形式去獲取數據。
GraphQL 執行邏輯
有人會問:
- 使用了 GraphQL 就要完全拋棄 REST 了嗎?
- GraphQL 需要直接對接數據庫嗎?
- 使用 GraphQL 需要對現有的後端服務進行大刀闊斧的修改嗎?
答案是:NO!不需要!
它完全可以以一種不侵入的方式來部署,將它作爲前後端的中間服務,也就是,現在開始逐漸流行的 前端 —— 中端 —— 後端 的三層結構模式來部署!
那就來看一下這樣的部署模式圖:
GraphQL 執行邏輯
也就是說,完全可以搭建一個 GraphQL 服務器,專門來處理前端請求,並處理後端服務獲取的數據,重新進行組裝、篩選、過濾,將完美符合前端需要的數據返回。
新的開發需求可以直接就使用 GraphQL 服務來獲取數據了,以前已經上線的功能無需改動,還是使用原有請求調用 REST 接口的方式,最低程度的降低更換 GraphQL 帶來的技術成本問題!
如果沒有那麼多成本來支撐改造,那麼就不需要改造!
只有當原有需求發生變化,需要對原功能進行修改時,就可以換成 GraphQL 了。
GraphQL 應用的基本架構
下圖是一個 GraphQL 應用的基本架構,其中客戶端只和 GraphQL 層進行 API 交互,而 GraphQL 層再往後接入各種數據源。這樣一來,只要是數據源有的數據, GraphQL 層都可以讓客戶端按需獲取,不必專門再去定接口了。
GraphQL 應用基本架構
一個 GraphQL 服務僅暴露一個 GraphQL Endpoint,可以按照業務來進行區分,部署多個 GraphQL 服務,分管不同的業務數據,這樣就可以避免單服務器壓力過大的問題了。
GraphQL 特點總結
- 聲明式數據獲取(可以對 API 進行查詢): 聲明式的數據查詢帶來了接口的精確返回,服務器會按數據查詢的格式返回同樣結構的 JSON 數據、真正照顧了客戶端的靈活性。
- **一個微服務僅暴露一個 GraphQL 層:**一個微服務只需暴露一個 GraphQL endpoint,客戶端請求相應數據只通過該端點按需獲取,不需要再額外定義其他接口。
- **傳輸層無關、數據庫技術無關:**帶來了更靈活的技術棧選擇,比如我們可以選擇對移動設備友好的協議,將網絡傳輸數據量最小化,實現在網絡協議層面優化應用。
Part 2 Schema & Type
GraphQL 支持的數據操作
GraphQL 對數據支持的操作有:
- **查詢(Query):**獲取數據的基本查詢。
- **變更(Mutation):**支持對數據的增刪改等操作。
- **訂閱(Subscription):**用於監聽數據變動、並靠 websocket 等協議推送變動的消息給對方。
GraphQL 支持的操作
GraphQL 的核心概念:圖表模式(Schema)
要想要設計 GraphQL 的數據模型,用來描述你的業務數據,那麼就必須要有一套 Schema 語法來做支撐。
想要描述數據,就必須離不開數據類型的定義。所以 GraphQL 設計了一套 Schema 模式(可以理解爲語法),其中最重要的就是數據類型的定義和支持。
那麼類型(Type)就是模式(Schema)最核心的東西了。
什麼是類型?
- 對於數據模型的抽象是通過類型(Type)來描述的,每一個類型有若干字段(Field)組成,每個字段又分別指向某個類型(Type)。這很像 Java、C# 中的類(Class)。
- GraphQL 的 Type 簡單可以分爲兩種,一種叫做 Scalar Type(標量類型),另一種叫做 Object Type(對象類型)。
那麼就分別來介紹下兩種類型。
標量類型(Scalar Type)
標量是 GraphQL 類型系統中最小的顆粒。類似於 Java、C# 中的基本類型。
其中內建標量主要有:
- String
- Int
- Float
- Boolean
- Enum
- ID
Scalar Type
上面的類型僅僅是 GraphQL 默認內置的類型,當然,爲了保證最大的靈活性,GraphQL 還可以很靈活的自行創建標量類型。
對象類型(Object Type)
僅有標量類型是不能滿足複雜抽象數據模型的需要,這時候我們可以使用對象類型。
通過對象模型來構建 GraphQL 中關於一個數據模型的形狀,同時還可以聲明各個模型之間的內在關聯(一對多、一對一或多對多)。
對象類型的定義可以參考下圖:
對象模型引入關聯關係
是不是很方便呢?我們可以像設計類圖一樣來設計 GraphQL 的對象模型。
類型修飾符(Type Modifier)
那麼,類型系統僅僅只有類型定義是不夠的,我們還需要對類型進行更廣泛性的描述。
類型修飾符就是用來修飾類型,以達到額外的數據類型要求控制。
比如:
- 列表:[Type]
- 非空:Type!
- 列表非空:[Type]!
- 非空列表,列表內容類型非空:[Type!]!
在描述數據模型(模式 Schema)時,就可以對字段施加限制條件。
例如定義了一個名爲 User 的對象類型,並對其字段進行定義和施加限制條件:
User 字段控制
那麼,返回數據時,像下面這種情況就是不允許的:
錯誤的表示
Graphql 會根據 Schema Type 來自動返回正確的數據:
正確的表示
其他類型
除了上面的,Graphql 還有一些其他類型來更好的引入面向對象的設計思想:
- **接口類型(Interfaces):**其他對象類型實現接口必須包含接口所有的字段,並具有相同的類型修飾符,纔算實現接口。
比如定義了一個接口類型:
那麼就可以實現該接口:
- **聯合類型(Union Types):**聯合類型和接口十分相似,但是它並不指定類型之間的任何共同字段。幾個對象類型共用一個聯合類型。
- **輸入類型(Input Types):**更新數據時有用,與常規對象只有關鍵字修飾不一樣,常規對象時 type 修飾,輸入類型是 input 修飾。
比如定義了一個輸入類型:
前端發送變更請求時就可以使用(通過參數來指定輸入的類型):
所以,這樣面向對象的設計方式,真的對後端開發人員特別友好!而且前端 MVVM 框架流行以來,面向對象的設計思想也越來越流行,前端使用 Graphql 也會得心應手。
Part 3 GraphQL 技術接入架構
Graphql 技術接入架構
那麼,該怎麼設計來接入我們現有的系統中呢?
- **將 Graphql 服務直連數據庫的方式:**最簡潔的配置,直接操作數據庫能減少中間環節的性能消耗。
直連數據庫的接入
- **集成現有服務的 GraphQL 層:**這種配置適合於舊服務的改造,尤其是在涉及第三方服務時、依然可以通過原有接口進行交互。
集成現有服務的 GraphQL 層
- **直連數據庫和集成服務的混合模式:**前兩種方式的混合。
混合接入方式
可以說是非常靈活了!你都不用擔心會給你帶來任何的麻煩。
服務端實現
在服務端, GraphQL 服務器可用任何可構建 Web 服務器的語言實現。有以下語言的實現供參考:
C# / .NET
Clojure
Elixir
Erlang
Go
Groovy
Java
JavaScript
Julia
Kotlin
Perl
PHP
Python
R
Ruby
Rust
Scala
Swift
種類繁多,幾乎流行的語言都有支持。
客戶端實現
在客戶端,Graphql Client 目前有下面的語言支持:
C# / .NET
Clojurescript
Elm
Flutter
Go
Java / Android
JavaScript
Julia
Swift / Objective-C iOS
Python
R
覆蓋了衆多客戶端設計語言,而其他語言的支持也在推進中。
Graphql 的一些服務
整理了下目前比較流行的服務框架:
- Apollo Engine: 一個用於監視 GraphQL 後端的性能和使用的服務。
- Graphcool (github): 一個 BaaS(後端即服務),它爲你的應用程序提供了一個 GraphQL 後端,且具有用於管理數據庫和存儲數據的強大的 web ui。
- Tipe (github): 一個 SaaS(軟件即服務)內容管理系統,允許你使用強大的編輯工具創建你 的內容,並通過 GraphQL 或 REST API 從任何地方訪問它。
- AWS AppSync:完全託管的 GraphQL 服務,包含實時訂閱、離線編程和同步、企業級安全特性以及細粒度的授權控制。
- Hasura:一個 BaaS(後端即服務),允許你在 Postgres 上創建數據表、定義權限並使用 GraphQL 接口查詢和操作。
Graphql 的一些工具
- graphiql (npm): 一個交互式的運行於瀏覽器中的 GraphQL IDE。
- Graphql Language Service: 一個用於構建 IDE 的 GraphQL 語言服務(診斷、自動完成等) 的接口。
- quicktype (github): 在 TypeScript、Swift、golang、C#、C++ 等語言中爲 GraphQL 查 詢生成類型。
想要獲取更多關於 Graphql 的一些框架、工具,可以去 awesome-graphql:一個神奇的社區,維護一系列庫、資源等,地址是
https://github.com/chentsulin/awesome-graphql。
想要學習更多 Graphql 的知識,可以去 GraphQL.cn。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://www.toutiao.com/i6833818331884028419