最近參與了一個大型項目,大型項目隨着系統業務量的增大,不同的應用和系統共同使用着許多的服務接口API,而隨着業務的變化和發展,不同的應用對相同資源的不同使用方法最終會導致需要維護的服務API數量呈現爆炸式的增長。而另一方面,創建一個大而全的通用性接口又非常不利於移動端使用(流量損耗),而且后端數據的無意義聚合也對整個系統帶來了很大的資源浪費。
1、 GrapQL背景
經查資料顯示GraphQL是Facebook 在2012年開發的,2015年開源,2016年下半年Facebook宣布可以在生產環境使用,而其內部早就已經廣泛應用了,用於替代 REST API。Facebook的解決方案:用一個“聰明”的節點來進行復雜的查詢,將數據按照客戶端的要求傳回去,后端根據GraphQL機制提供一個具有強大功能的接口,用以滿足前端數據的個性化需求,既保證了多樣性,又控制了接口數量。
2、 使用介紹
GraphQL並不是一門程序語言或者框架,它是描述請求數據的一種規范,是協議而非存儲,GraphQL本身並不直接提供后端存儲的能力,它不綁定任何的數據庫或者存儲引擎,它可以利用已有的代碼和技術來進行數據源管理。
一個GraphQL查詢是一個被發往服務端的字符串,該查詢在服務端被解釋和執行后返回JSON數據給客戶端。作為服務端除了了解具體的生態之外還需要了解緩存、上傳文件等,如果是系統遷移還需要了解特定的框架,作為系統開發推薦使用Prisma的框架。偏向客戶端方向的話,需要進一步了解關於graphql-client的相關知識,推薦使用apollo。
3、 優缺點
GraphQL雖然能夠代替廣泛使用的REST API,但也是各有千秋。
3.1優點:
RESTful:服務端決定有哪些數據獲取方式,客戶端只能挑選使用,如果數據過於冗余也只能默默接收再對數據進行處理;而數據不能滿足需求則需要請求更多的接口。
GraphQL:給客戶端自主選擇數據內容的能力,客戶端完全自主決定獲取信息的內容,服務端負責精確的返回目標數據。提供一種更嚴格、可擴展、可維護的數據查詢方式。
3.2缺點:
重構:使用GraphQL對數據源進行管理,相當於要對整個服務端進行一次換血,考慮的不僅僅是需要針對現有數據源建立一套GraphQL的類型系統,同時需要改造服務端暴露數據的方式,這對業務久遠的產品無疑是一場災難。
查詢性能:GraphQL查詢的每個字段如果都有自己的resolve方法,可能導致一次查詢操作對數據庫跑了大量的query。
4、 Type類型
在GraphQL中,我們通過預先定義一張Schema和聲明一些Type來達到預期的效果,
首先我們需要知道:
- 對於數據模型的抽象是通過Type來描述的
- 對於接口獲取數據的邏輯是通過Schema來描述的
GraphQL的Type簡單可以分為兩種,一種叫做Scalar Type(標量類型),另一種叫做Object Type(對象類型)。
注意:標量是GraphQL類型系統的最小顆粒。
4.1標量類型
GraphQL中的內建的標量包含String、Int、Float、Boolean、Enum。也可以通過Scalar聲明一個新的標量:
a、 Prisma中,還有DateTime和ID這兩個標量分別代表日期格式和主鍵。(prisma一個使用GraphQL來抽象數據庫操作的庫)
b、 Upload標量來代表要上傳的文件
4.2高級數據類型
復雜的數據模型:Object、Interface、Union、Enum、Input Object、List、Non-Null(Object、Interface 和 Union 三種類型是不能作為輸入對象類型的)。
總之,我們通過對象模型來構建GraphQL中關於一個數據模型的形狀,同時還可以聲明各個模型之間的內在關聯(一對多、一對一或多對多)。
4.3類型修飾符
當前的類型修飾符有兩種,分別是List和Required ,它們的語法分別為[Type]和Type!, 同時這兩者可以互相組合,比如[Type]!或者[Type!]或者[Type!]!(請仔細看這里!的位置),它們的含義分別為:
- 列表本身為必填項,但其內部元素可以為空
- 列表本身可以為空,但是其內部元素為必填
- 列表本身和內部元素均為必填
5、 Schema邏輯
我們其實不妨把它當做REST架構中每個獨立資源的URL來理解它,只不過在GraphQL中,我們用Query來描述資源的獲取方式。因此,我們可以將Schema理解為多個Query組成的一張表。
5.1 定義Query類型
GraphQL中使用Query來抽象數據的查詢邏輯,當前標准下,有三種查詢類型,分別是query(查詢)、mutation(更改)和subscription(訂閱),這三種查詢類型必須是Root Query(根查詢)存在的。
注:Query特指GraphQL中的邏輯查詢(包含三種類型),query指GraphQL中的查詢類型(僅指查詢類型)對於傳統的CRUD項目使用query(查詢)、mutation(更改)就夠了,subscription(訂閱)是針對real-time應用提出。
5.2 Resolver解析函數
Schema中聲明了若干Query,那么我們只進行了一半的工作,因為我們並沒有提供相關Query所返回數據的邏輯。在GraphQL中,我們會有這樣一個約定,Query和與之對應的Resolver是同名的,這樣在GraphQL才能把它們對應起來。大體的解析流程就是遇到一個Query之后,嘗試使用它的Resolver取值,之后再對返回值進行解析,這個過程是遞歸的,直到所解析Field的類型是Scalar Type(標量類型)為止。
Resolver本身的聲明在各個語言中是不一樣的,因為它代表數據獲取的具體邏輯。它的函數簽名(以js為例子)如下:function(parent, args, ctx, info) {}
其中的參數的意義如下:
parent: 當前上一個Resolver的返回值
args: 傳入某個Query中的函數(比如上面例子中article(id: Int)中的id)
ctx: 在Resolver解析鏈中不斷傳遞的中間變量(類似中間件架構中的context)
info: 當前Query的AST對象
注意:Resolver內部實現對於GraphQL完全是黑盒狀態。這意味着Resolver如何返回數據、返回什么樣的數據、從哪返回數據,完全取決於Resolver本身,基於這一點,在實際中,很多人往往把GraphQL作為一個中間層來使用,數據的獲取通過Resolver來封裝,內部數據獲取的實現可能基於RPC、REST、WS、SQL等多種不同的方式。
好了,GraphOL基礎今天我就總結在這里,關於其實戰應用且等下回分曉。