摘要:系列文章核心概念本文淺嘗最近因為工作上新產(chǎn)品的需要,讓我有機會了解和嘗試。這篇文章主要分享的是的核心概念,主要分為和四部分。再次強調(diào),本文主要講的是的核心概念,中所定義的類,都是設(shè)計類,并不是具體實現(xiàn)代碼。
A query language created by Facebook for describing data requirements on complex application data models
系列文章:
GraphQL 核心概念(本文)
graphql-js 淺嘗
最近因為工作上新產(chǎn)品的需要,讓我有機會了解和嘗試 GraphQL。按照套路,在介紹一項新技術(shù)的時候總要回答 3 個問題:What, Why & How。
What is GraphQL?正如副標題所說,GraphQL 是由 Facebook 創(chuàng)造的用于描述復雜數(shù)據(jù)模型的一種查詢語言。這里查詢語言所指的并不是常規(guī)意義上的類似 sql 語句的查詢語言,而是一種用于前后端數(shù)據(jù)查詢方式的規(guī)范。
Why using GraphQL?當今客戶端和服務(wù)端主要的交互方式有 2 種,分別是 REST 和 ad hoc 端點。GraphQL 官網(wǎng)指出了它們的不足之處主要在于:當需求或數(shù)據(jù)發(fā)生變化時,它們都需要建立新的接口來適應(yīng)變化,而不斷添加的接口,會造成服務(wù)器代碼的不斷增長,即使通過增加接口版本,也并不能夠完全限制服務(wù)器代碼的增長。(更多不足,前往官網(wǎng)查看)
既然,GraphQL 指出了它們的缺點,那么它自然解決了這些問題。
如何解決的哪?那就得說說 GraphQL 的 3 大特性。
首先,它是聲明式的。查詢的結(jié)果格式由請求方(即客戶端)決定而非響應(yīng)方(即服務(wù)器端)決定,也就是說,一個 GraphQL 查詢結(jié)果的返回是同客戶端請求時的結(jié)構(gòu)一樣的,不多不少,不增不減。
其次,它是可組合的。一個 GraphQL 的查詢結(jié)構(gòu)是一個有層次的字段集,它可以任意層次地進行嵌套或組合,也就是說它可以通過對字段進行組合、嵌套來滿足需求。
第三,它是強類型的。強類型保證,只有當一個 GraphQL 查詢滿足所設(shè)定的查詢類型,那么查詢的結(jié)果才會被執(zhí)行。
回到之前的問題,也就是說,當需求或數(shù)據(jù)發(fā)生變化時,客戶端可以根據(jù)需求來改變查詢的結(jié)構(gòu),只要查詢結(jié)構(gòu)滿足之前的定義,服務(wù)器端代碼甚至不需要做任何的修改;即使不滿足,也只需修改服務(wù)器端的查詢結(jié)構(gòu),而不必額外添加新的接口來滿足需求。
Core Concepts可能你會問,按套路這節(jié)不該是 HOW to use GraphQL,怎么變成了 Core Concepts?
由于,GraphQL 是一種規(guī)范,于是,它的實現(xiàn)不限制某種特定語言,每種語言對 GraphQL 都可以有自己的實現(xiàn),比如相對 JavaScript 就有 graphql-js。既然,實現(xiàn)都不相同,那么,使用的方法也會不同,所以便不在這里細述了。
這篇文章主要分享的是 GraphQL 的核心概念,主要分為:Type System, Query Syntax, Validation 和 Introspection 四部分。
Type System類型系統(tǒng)是整個 GraphQL 的核心,它用來定義每個查詢對象和返回對象的類型,將所有定義的對象組合起來就形成了一整個 GraphQL Schema。
這個概念比較抽象,空說很難理解,還是拿例子來邊看邊說。個人博客相信大家都很熟悉,這里就嘗試用一個簡單的博客系統(tǒng)的例子來說明,這會比官網(wǎng)星戰(zhàn)的例子簡單一點。
Let"s go!
既然是一個博客,那么,文章肯定少不了,我們首先來建立一個文章的類型。
type Post { id: String, name: String, createDate: String, title: String, subtitle: String, content: String }
這樣,一個簡單的文章類型就定義好了,它是一個自定義的類型,包含了一系列的字段,巧合的是這些字段的類型正好都是 String(字符串類型)。
String 沒有定義過,為什么可以直接使用哪?因為,String 是 GraphQL 支持的 scalar type(標量類型),默認的標量類型還包括 Int,Float, Boolean 和 ID。
許多的博客網(wǎng)站都支持給每篇文章打標簽,那么我們在來建立一個標簽的類型。
type Tag { id: String, name: String, label: String, createDate: String }
標簽類型和文章類型怎么整合到一起哪?
GraphQL 不單單支持簡單類型,還支持一些其他類型,如 Object, Enum, List, NotNull 這些常見的類型,還有 Interface, Union, InputObject 這幾個特殊類型。
PS:一直沒搞明白 Interface 和 Union 的區(qū)別在哪,它們分別適用于什么場景?谷歌了一下,還真有篇文章說它們的區(qū)別,不過恕我愚鈍,還是沒能領(lǐng)悟,還望大神點撥...
再修改一下之前的文章類型,使一個文章可以包含多個標簽。
type Post { id: String, name: String, createDate: String, title: String, subtitle: String, content: String, tags: [Tag] }
通常在博客網(wǎng)站的標簽列表中會顯示該標簽下的一些文章,由于 GraphQL 是以產(chǎn)品為中心的,那么在標簽類型下也可以有文章類型。于是,標簽類就變成了
type Tag { id: String, name: String, label: String, createDate: String, posts: [Post] }
可能你會疑惑,文章類型和標簽類型這樣相互嵌套會不會造成死循環(huán)?我可以負責任的告訴你:不會。你可以盡情地嵌套、組合類型結(jié)構(gòu)來滿足你的需求。
最后,根據(jù)整個博客網(wǎng)站的需求,組合嵌套剛剛定義的文章類型和標簽類型,建立一個根類型作為查詢的 schema。
type Blog { post: Post, // 查詢一篇文章 posts: [Post], // 用于博客首頁,查詢一組文章 tag: Tag, // 查詢一個標簽 tags: [Tag], // 用于博客標簽頁,查詢所有標簽 }
OK,我們的類型和 schema 都定義好了,就可以開始查詢了。怎么查哪?那我們來看看 GraphQL 的查詢語法。
Query SyntaxGraphQL 的查詢語法同我們現(xiàn)在所使用的有一大不同是,傳輸?shù)臄?shù)據(jù)結(jié)構(gòu)并不是 JSON 對象,而是一個字符串,這個字符串描述了客戶端希望服務(wù)端返回數(shù)據(jù)的具體結(jié)構(gòu)。
知道了概念,那么一個 GraphQL 的查詢到底長什么樣哪?繼續(xù)我們的例子,假設(shè),我們現(xiàn)在要查詢一篇文章,那么,GraphQL 的查詢語句就可以是這樣。
query FetchPostQuery { post { id, name, createDate, title, subtitle, content, tags { name, label } } }
它相對應(yīng)的返回就會是類似這樣的一個 JSON 數(shù)據(jù)。
{ "data": { "post": { "id": "3", "name": "graphql-core-concepts", "createDate": "2016-08-01", "title": "GraphQL 核心概念", "subtitle": "A query language created by Facebook for decribing data requirements on complex application data models", "content": "省略...", "tags": [{ "name": "graphql", "label": "GraphQL" }] } } }
從中我們可以看到,數(shù)據(jù)返回了整個文章的屬性以及部分的標簽屬性。其中,標簽屬性并沒有返回全部的字段,而是只返回了 name 和 label 字段的屬性,做到了返回數(shù)據(jù)的結(jié)構(gòu)完成同請求數(shù)據(jù)的結(jié)構(gòu)相同,沒有冗余的數(shù)據(jù)。
查詢添加參數(shù)的需求也非?;?,在 GraphQL 的查詢語法中也相當簡單,就拿剛剛的例子,要查詢特定的文章就可以把它改成這樣。
query FetchPostQuery { post(name: "graphql-core-concepts") { id, name, createDate, title, subtitle, content, tags { name, label } } }
返回的結(jié)果會是和之前的一樣。查詢關(guān)鍵字只有在多個查詢時才必須,在單個查詢時可以省略。同時,也可以對查詢的返回起別名,再來看看博客的首頁希望展示一個粗略的文章列表,那么這樣的一個查詢語句可以是
{ postList: posts { id, name, createDate, title, subtitle, tags { name, label } } }
這里,我們省略了查詢關(guān)鍵字,并將 posts 起了一個別名為 postList,返回的結(jié)果就會是
{ "data": { "postList": [{ "id": "3", "name": "graphql-core-concepts", "createDate": "2016-08-01", "title": "GraphQL 核心概念", "subtitle": "A query language created by Facebook for decribing data requirements on complex application data models", "tags": [{ "name": "graphql", "label": "GraphQL" }] }, { "id": "2", "name": "redux-advanced", "createDate": "2016-07-23", "title": "Redux 進階", "subtitle": "Advanced skill in Redux", "tags": [{ "name": "javascript", "label": "JavaScript" }, { "name": "redux", "label": "Redux" }, { "name": "state-management", "label": "State management" }, { "name": "angular-1.x", "label": "Angular 1.x" }, { "name": "ui-router", "label": "ui-router" }, { "name": "redux-ui-router", "label": "redux-ui-router" }] }, { "id": "1", "name": "getting-started-with-redux", "createDate": "2016-07-06", "title": "Redux 入門", "subtitle": "A tiny predictable state management lib for JavaScript apps", "tags": [{ "name": "javascript", "label": "JavaScript" }, { "name": "redux", "label": "Redux" }, { "name": "state-management", "label": "State management" }, { "name": "angular-1.x", "label": "Angular 1.x" }] }] } }
同樣,查詢所有標簽的語句就可以是這樣
{ tags { id, name, label, posts { name, title } } }
這樣,一個 GraphQL 的接口,滿足了一個簡單博客網(wǎng)站的所有需求,是不是很神奇?
Validation由于 GraphQL 是一個強類型語言,所以它可以在執(zhí)行查詢之前檢查每個查詢語句是否滿足事先設(shè)定的 schema,符合則合法,如果查詢語句不合法則不進行查詢。
以上所舉的都是合法的例子,官網(wǎng)上舉了一些例子,這里就不貼了,我們就總結(jié)看看要注意的有哪幾點。
fragment 不能引用自己從而形成一個循環(huán)
不能查詢類型中不存在的字段
查詢的字段如果不是 scalar type(標量類型)或 enum type(枚舉類型),則需要明確該字段下所包含的字段
同上一條相對,如果查詢字段是 scalar type(標量類型),那么它就不能再有子字段
IntrospectionIntrospection 這個詞的意思是內(nèi)省,自我檢查(第一次發(fā)現(xiàn)英語有語義如此豐富的詞,又暴露詞匯量少了-_-||)。
不扯遠了,在 GraphQL 中 Introspection 是一個非常有用的功能,它可以用來查詢當前 GraphQL 的 schema,從而得知服務(wù)器端支持何種類型的查詢。
這是一個非常強大且有用的功能,可以想象一下,現(xiàn)在大型公司的開發(fā)基本上都是前后端分離的,客戶端并不知道服務(wù)器端提供的 schema 結(jié)構(gòu),但通過 Introspection,客戶端就能獲得當前服務(wù)器端所提供的 schema,這無論對開發(fā),還是調(diào)試錯誤都很有幫助。
還是拿剛剛的博客系統(tǒng)來做例子,我們可以通過查詢 __schema 字段來獲得當前所支持的查詢類型。
// query string { __schema { types { name } } } // response data { "data": { "__schema": { "types": [ { "name": "String" }, { "name": "BlogType" }, { "name": "PostType" }, { "name": "ID" }, { "name": "TagType" }, { "name": "__Schema" }, { "name": "__Type" }, { "name": "__TypeKind" }, { "name": "Boolean" }, { "name": "__Field" }, { "name": "__InputValue" }, { "name": "__EnumValue" }, { "name": "__Directive" }, { "name": "__DirectiveLocation" } ] } } }
從返回的數(shù)據(jù)中可以看到,我們自定義的 BlogType, PostType 和 TagType 類,剩下的都是 GraphQL 內(nèi)部類型,其中又分為兩類:一類是 ID, String 和 Bealoon 所表示的標量類型,另一類以雙下劃線開頭的是用于自我檢查的類型。
知道了自定義類,假設(shè),還想知道自定義類中包含哪些屬性以及屬性的類型,就可以這樣查詢
// query string { __type(name: "PostType") { name fields { name, type { name, kind } } } } // response result { "data": { "__type": { "name": "PostType", "fields": [ { "name": "id", "type": { "name": null, "kind": "NON_NULL" } }, { "name": "name", "type": { "name": null, "kind": "NON_NULL" } }, { "name": "createDate", "type": { "name": null, "kind": "NON_NULL" } }, { "name": "title", "type": { "name": null, "kind": "NON_NULL" } }, { "name": "subtitle", "type": { "name": "String", "kind": "SCALAR" } }, { "name": "content", "type": { "name": "String", "kind": "SCALAR" } }, { "name": "tags", "type": { "name": null, "kind": "LIST" } } ] } } }最后
總結(jié)一下,GraphQL 是一種客戶端同服務(wù)端之間數(shù)據(jù)交互的概念,具有強大、靈活、易擴展等的特點。既然,它是一種概念,那么,不同的語言就可以有各種不同的實現(xiàn)方式。
概念并不多,在于靈活運用。
PS:再次強調(diào),本文主要講的是 GraphQL 的核心概念,Type System 中所定義的類,都是設(shè)計類,并不是具體實現(xiàn)代碼。實現(xiàn)請聽下回分解。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/90821.html
摘要:系列文章核心概念淺嘗本文常言道,實踐是檢驗真理的唯一標準。遵循傳統(tǒng),第一個例子必須是。官方提供這個中間件來支持基于的查詢,所以,這里選用作為服務(wù)器。首先是,這里對做了一點小修改,給幾個字段添加了不能為空的設(shè)計。 系列文章: GraphQL 核心概念 graphql-js 淺嘗(本文) 常言道,實踐是檢驗真理的唯一標準。 上一篇文章講了 GraphQL 的核心概念,所提到的一些例...
摘要:我在工程實踐中直接使用類作為對應(yīng)實體類的。因此我的結(jié)論是,此庫并不適用于我的工程實踐。工程實踐中對其應(yīng)用方式的考慮在的官方教程中建議針對每請求創(chuàng)建新的實例,查詢請求結(jié)束則實例們的生命周期結(jié)束。 因為自己寫過基于react的前端應(yīng)用,因此一看到GraphQL就被深深吸引,真是直擊痛點??!服務(wù)端開發(fā)一直是基于java, Spring的,因此開始研究如何在現(xiàn)有工程框架下加入graphql的支...
摘要:我們知道是一種從服務(wù)器公開數(shù)據(jù)的流行方式。描述所有的可能類型系統(tǒng)基于類型和字段的方式進行組織,而非入口端點。因此,需要對后端進行調(diào)整,以滿足新的數(shù)據(jù)需求,這會降低生產(chǎn)力并顯著降低將用戶反饋集成到產(chǎn)品中的能力。 showImg(https://segmentfault.com/img/remote/1460000017875905?w=2234&h=974); 在前幾天的《StateOf...
摘要:我們知道是一種從服務(wù)器公開數(shù)據(jù)的流行方式。描述所有的可能類型系統(tǒng)基于類型和字段的方式進行組織,而非入口端點。因此,需要對后端進行調(diào)整,以滿足新的數(shù)據(jù)需求,這會降低生產(chǎn)力并顯著降低將用戶反饋集成到產(chǎn)品中的能力。 showImg(https://segmentfault.com/img/remote/1460000017875905?w=2234&h=974); 在前幾天的《StateOf...
摘要:我們知道是一種從服務(wù)器公開數(shù)據(jù)的流行方式。描述所有的可能類型系統(tǒng)基于類型和字段的方式進行組織,而非入口端點。因此,需要對后端進行調(diào)整,以滿足新的數(shù)據(jù)需求,這會降低生產(chǎn)力并顯著降低將用戶反饋集成到產(chǎn)品中的能力。 showImg(https://segmentfault.com/img/remote/1460000017875905?w=2234&h=974); 在前幾天的《StateOf...
閱讀 2616·2021-11-22 15:25
閱讀 1445·2021-11-15 17:59
閱讀 1146·2021-09-29 09:34
閱讀 1556·2021-09-26 09:46
閱讀 3039·2021-09-02 15:40
閱讀 1198·2019-08-30 15:56
閱讀 3292·2019-08-30 15:55
閱讀 702·2019-08-29 17:08