作者: 一字馬胡
轉(zhuǎn)載標(biāo)志 【2017-11-13】
更新日志
| 日期 | 更新內(nèi)容 | 備注 |
|---|---|---|
| 2017-11-13 | 新建文章 | 初版 |
導(dǎo)入
作為一種強(qiáng)大的DSQL,學(xué)習(xí)GraphQL的意義是非常大的,為了迅速了解GraphQL,可以參考文章GraphQL初探:一種強(qiáng)大的DSQL,該文章給出了一個(gè)使用GraphQL的簡(jiǎn)單示例demo。GraphQL不僅可以面向傳統(tǒng)的數(shù)據(jù)庫(kù)(比如Mysql,NoSql),還可以面向微服務(wù),以及其他的數(shù)據(jù)源,或者面向數(shù)據(jù)庫(kù)、微服務(wù)等混合數(shù)據(jù)源,GraphQL試圖抽象服務(wù)端的數(shù)據(jù)成為一個(gè)綜合的數(shù)據(jù)庫(kù),而前端(客戶端)的請(qǐng)求就是查詢數(shù)據(jù)庫(kù),這樣做很明顯的一個(gè)好處是前端(客戶端)可以根據(jù)自己的需要來(lái)查詢(就像是請(qǐng)求mysql數(shù)據(jù)庫(kù),只輸出需要的列),這稱(chēng)為“精準(zhǔn)查詢”,使用GraphQL的另外一個(gè)明顯的好處是一次請(qǐng)求即可獲取所需要的數(shù)據(jù),對(duì)于請(qǐng)求REST API來(lái)說(shuō),每次請(qǐng)求返回的數(shù)據(jù)格式是相對(duì)固定的,沒(méi)有太多的靈活性,并且可能為了將接口變薄,會(huì)根據(jù)數(shù)據(jù)屬性等將接口拆分開(kāi)來(lái),所以每個(gè)接口可能只能返回一部分需要的內(nèi)容,所以就需要客戶端來(lái)請(qǐng)求多次來(lái)獲取到所有的數(shù)據(jù),這樣就造成了一些延時(shí)。下面的圖片展示了GraphQL如何將多種數(shù)據(jù)源組合起來(lái)為前端(客戶端)提供統(tǒng)一的API(使用GraphQL來(lái)設(shè)計(jì)API只需要提供一個(gè)端點(diǎn)):

本文依然不會(huì)對(duì)GraphQL的原理做太多分析總結(jié),學(xué)習(xí)需要一步一步來(lái),上一篇文章僅僅寫(xiě)了一個(gè)可運(yùn)行的demo,本文想要借助現(xiàn)有的實(shí)現(xiàn)來(lái)更加深入的了解一些關(guān)于GraphQL的內(nèi)容,為后面可以自主設(shè)計(jì)GraphQL API提供支持。Github 的V4 API借助了GraphQL來(lái)設(shè)計(jì),具有很高的研究?jī)r(jià)值,所以本文就介紹Github 的V4 API,并且介紹如何使用V4接口來(lái)從Github的服務(wù)器獲取我們想要的數(shù)據(jù),因?yàn)镚ithub V4 API只提供了一個(gè)端點(diǎn):https://api.github.com/graphql,所以很大程度上降低了我們的認(rèn)知障礙,并且Github提供了在線查詢界面:Github V4 API Explorer,你可以在左側(cè)的輸入框中輸入GraphQL查詢語(yǔ)句,點(diǎn)擊執(zhí)行就可以在右側(cè)看到執(zhí)行結(jié)果,非常直觀,并且提供了完整的接口文檔,可以獲取任意我們想要獲取的數(shù)據(jù),本文將首先介紹一些Github V4 API中的新概念,然后基于Github V4 API Explorer來(lái)介紹如何獲取我們想要的數(shù)據(jù)。
Github V4 API中的一些新概念
Schema
首先是Schema,在GraphQL中Schema是一個(gè)很重要的概念,Schema定義了GraphQL API的類(lèi)型系統(tǒng),它完整的描述了前端(或者客戶端)可以從服務(wù)端獲取的所有數(shù)據(jù)內(nèi)容,前端或者客戶端的GraphQL查詢請(qǐng)求將根據(jù)Schema進(jìn)行校驗(yàn)和執(zhí)行,客戶端可以通過(guò)“自省”(introspection)獲取關(guān)于schema的信息。schema存放于GraphQL API服務(wù)器。關(guān)于如何生成一個(gè)schema可以參考本文開(kāi)篇的文章鏈接。
Field
Field是客戶端可以從服務(wù)端獲取的某個(gè)內(nèi)容,比如某個(gè)字段,或者某個(gè)對(duì)象等,GraphQL查詢語(yǔ)言本質(zhì)上就是從對(duì)象中選擇field,所有的GraphQL操作必須指明到最底層的field,并且返回值為標(biāo)量,以確保響應(yīng)結(jié)果的結(jié)構(gòu)明白無(wú)誤,所謂標(biāo)量(scalar),也就是基本數(shù)據(jù)類(lèi)型,比如String、int等。如果你嘗試返回一個(gè)不是標(biāo)量的field,schema校驗(yàn)將會(huì)拋出錯(cuò)誤。你必須添加嵌套的內(nèi)部field直至所有的field都返回標(biāo)量,這一點(diǎn)很重要,需要牢牢記住。
Connection
還記得GraphQL的標(biāo)志嗎?它是一個(gè)圖,由頂點(diǎn)和邊構(gòu)成,所謂Connection,就是可以關(guān)聯(lián)查詢,GraphQL就是通過(guò)Connection來(lái)實(shí)現(xiàn)只需要一次請(qǐng)求就可以獲取全部需要的數(shù)據(jù)的功能的,客戶端可以通過(guò)Connection來(lái)進(jìn)行關(guān)聯(lián)查詢,比如在查找一篇文章的信息的時(shí)候,除了文章的標(biāo)題、作者、文字內(nèi)容、圖片內(nèi)容、閱讀數(shù)等信息外,可以通過(guò)Connection查詢?cè)撐恼孪旅娴脑u(píng)論信息,而在評(píng)論中,可以通過(guò)Connection根據(jù)評(píng)論人的id來(lái)獲取用戶的信息,這就是Connection的威力,下文中將會(huì)把Connection的威力體現(xiàn)出來(lái)。
Edge
Edge是GraphQL中的邊,它表示Node之間的Connection,當(dāng)你查詢一個(gè)connection時(shí),你通過(guò)edge到達(dá)node。每個(gè)edgesfield都有一個(gè)nodefield和一個(gè)cursorfield。cursor是用來(lái)分頁(yè)的。
Node
Node是一個(gè)對(duì)象,它就是我們獲取數(shù)據(jù)的節(jié)點(diǎn),比如用戶信息的對(duì)象就是一個(gè)節(jié)點(diǎn),或者一篇文章的信息就是一個(gè)節(jié)點(diǎn),如果正在查詢的Node不是標(biāo)量的話,那么我們需要指定Node中的Field直到返回的是標(biāo)量類(lèi)型為止。
Github V4 API實(shí)戰(zhàn)
上文中介紹了一些關(guān)于Github V4 API的相關(guān)內(nèi)容,本文剩下的部分將清楚明白的介紹如何使用Github V4 API來(lái)獲取我們想要的數(shù)據(jù),通過(guò)本文的介紹,你應(yīng)當(dāng)很清晰的認(rèn)識(shí)到GraphQL帶來(lái)的福利,以及Github V4 API為何要選擇GraphQL來(lái)進(jìn)行設(shè)計(jì)。首選,你需要打開(kāi)Github V4 API Explorer,下面的圖片展示了你應(yīng)當(dāng)看到的界面:

左側(cè)是GraphQL查詢語(yǔ)句的輸入框,中間是執(zhí)行結(jié)果,右側(cè)是GraphQL查詢文檔,配合這三個(gè)區(qū)域,我們可以很方便的獲取任意我們想要獲取的數(shù)據(jù),下面由淺入深的介紹如何從Github獲取數(shù)據(jù)。首先是最簡(jiǎn)單的一個(gè)例子,我想獲取當(dāng)前登錄的賬號(hào)的一些基本信息,包括賬號(hào)名字,郵箱,id,下面是查詢語(yǔ)句:
query {
viewer {
login
url
id
}
}
查詢的結(jié)果如下:
{
"data": {
"viewer": {
"login": "pandening",
"url": "https://github.com/pandening",
"id": "MDQ6VXNlcjE2MjI1Nzk2"
}
}
}
如果現(xiàn)在我想要獲取我的簽名呢?只需要在查詢語(yǔ)句中增加一個(gè)Field就可以了:
query {
viewer {
login
url
id
bio
}
}
返回的結(jié)果如下:
{
"data": {
"viewer": {
"login": "pandening",
"url": "https://github.com/pandening",
"id": "MDQ6VXNlcjE2MjI1Nzk2",
"bio": "/╲/\\╭? oo??oo ?╮/\\╱\\"
}
}
}
從返回的結(jié)果可以看出,我的登錄賬號(hào)的名字叫做pandening,我的主頁(yè)url為https://github.com/pandening,id為MDQ6VXNlcjE2MjI1Nzk2,我的簽名為“/╲/\╭? oo??oo ?╮/\╱\”(非主流啊),可以發(fā)現(xiàn)返回的json和查詢的輸入是一對(duì)一的,這就是所謂的精準(zhǔn)查詢,不多不少,正好合適。那現(xiàn)在來(lái)說(shuō)說(shuō)如何寫(xiě)一個(gè)查詢呢?比如我上面的查詢語(yǔ)句是怎么寫(xiě)出來(lái)的呢?這就的配合右側(cè)的接口文檔了,點(diǎn)開(kāi)文檔可以看到有兩種類(lèi)型的操作,一種是Query,用于查詢數(shù)據(jù),另外一種是Mutation,用于向服務(wù)端發(fā)送寫(xiě)請(qǐng)求,本文主要關(guān)注的是讀數(shù)據(jù),也就是Query操作,關(guān)于Mutation的相關(guān)內(nèi)容也是類(lèi)似的。點(diǎn)開(kāi)Query,我們可以看到下面的內(nèi)容:

上面我使用了viewer來(lái)查詢當(dāng)前登錄的Github的信息,所以點(diǎn)開(kāi)viewer,就可以看到我們可以獲取的Field了,如果該Field為標(biāo)量類(lèi)型,那么可以直接返回,比如id,如果某個(gè)Field不是標(biāo)量的,比如followers,它就不是一個(gè)標(biāo)量,那么就要一直查詢直到返回標(biāo)量為止。上面的例子很簡(jiǎn)單,但是通過(guò)這個(gè)例子可以開(kāi)始結(jié)合接口文檔來(lái)進(jìn)行復(fù)雜的查詢了,下面,有一個(gè)需求,需要獲取當(dāng)前賬號(hào)的名字,url,簽名,加入Github的時(shí)間,郵箱,以及該賬號(hào)的前5個(gè)followers(賬號(hào)名、bio、郵箱、加入Github的時(shí)間),當(dāng)前賬號(hào)的前5個(gè)倉(cāng)庫(kù)(名字、star信息、fork信息),下面是構(gòu)造出來(lái)的GraphQL查詢語(yǔ)句:
query {
viewer {
login
url
bio
email
createdAt
followers(first : 5) {
edges {
node {
name
bio
email
createdAt
}
}
}
repositories(first : 5, isFork : false) {
edges {
node {
name
stargazers (first : 10){
edges {
starredAt
node {
name
}
}
}
forks (first : 10){
edges {
node {
createdAt
name
}
}
}
}
}
}
}
}
返回的結(jié)果如下:
{
"data": {
"viewer": {
"login": "pandening",
"url": "https://github.com/pandening",
"bio": "/╲/\\╭? oo??oo ?╮/\\╱\\",
"email": "1425124481@qq.com",
"createdAt": "2015-12-09T14:17:52Z",
"followers": {
"edges": [
{
"node": {
"name": "Wang Weitao",
"bio": "",
"email": "softweitao@126.com",
"createdAt": "2012-10-16T16:02:17Z"
}
},
{
"node": {
"name": "Nthan",
"bio": "",
"email": "664157212@qq.com",
"createdAt": "2013-01-16T02:21:07Z"
}
},
{
"node": {
"name": "Mateusz Bagiński",
"bio": "Python/JavaScript Developer",
"email": "cziken58@gmail.com",
"createdAt": "2013-03-23T12:49:03Z"
}
},
{
"node": {
"name": "Dalin Huang",
"bio": "these violent delights have violent ends",
"email": "dhuan023@gmail.com",
"createdAt": "2014-01-26T20:07:36Z"
}
},
{
"node": {
"name": "Ramsey",
"bio": "Arsenal Fan",
"email": "kiminh@sjtu.edu.cn",
"createdAt": "2014-07-18T13:01:22Z"
}
}
]
},
"repositories": {
"edges": [
{
"node": {
"name": "HJSTL",
"stargazers": {
"edges": [
{
"starredAt": "2016-05-08T04:12:01Z",
"node": {
"name": "Jian Hu"
}
}
]
},
"forks": {
"edges": [
{
"node": {
"createdAt": "2017-01-11T02:14:01Z",
"name": "hjstl"
}
}
]
}
}
},
{
"node": {
"name": "AcppLib",
"stargazers": {
"edges": [
{
"starredAt": "2016-08-21T03:33:14Z",
"node": {
"name": "Jian Hu"
}
}
]
},
"forks": {
"edges": []
}
}
},
{
"node": {
"name": "poj-solution",
"stargazers": {
"edges": [
{
"starredAt": "2016-08-22T12:20:41Z",
"node": {
"name": "Jian Hu"
}
}
]
},
"forks": {
"edges": [
{
"node": {
"createdAt": "2016-12-29T21:18:04Z",
"name": "poj-solution"
}
}
]
}
}
},
{
"node": {
"name": "storm-example-onlineusers",
"stargazers": {
"edges": [
{
"starredAt": "2016-08-22T12:24:23Z",
"node": {
"name": "Jian Hu"
}
}
]
},
"forks": {
"edges": []
}
}
},
{
"node": {
"name": "storm-jsoup-spider",
"stargazers": {
"edges": [
{
"starredAt": "2016-08-24T04:40:58Z",
"node": {
"name": "Jian Hu"
}
}
]
},
"forks": {
"edges": []
}
}
}
]
}
}
}
}
看著很復(fù)雜,但是只要照著右側(cè)的GraphQL接口文檔就可以快速的組裝出我們需要的數(shù)據(jù)了,當(dāng)然,你可以組裝任意復(fù)雜的查詢語(yǔ)句,只要符合GraphQL的查詢語(yǔ)句的要求,都可以從Github 獲取數(shù)據(jù),但是需要注意的一點(diǎn)是,GraphQL確實(shí)可以一次性獲取很全面的數(shù)據(jù),但是也需要考慮響應(yīng)時(shí)間的問(wèn)題,不能一次性獲取了大量的數(shù)據(jù),但是延時(shí)很高,這樣的應(yīng)用的用戶體驗(yàn)是很差的,需要權(quán)衡一下。
結(jié)語(yǔ)
本文是關(guān)于GraphQL系列的第二篇文章,主要介紹了Github的V4 API中的一些新概念,以及如何使用V4 API來(lái)獲取我們想要的數(shù)據(jù),文中首先介紹了一個(gè)比較簡(jiǎn)單的查詢,然后是一個(gè)相對(duì)復(fù)雜的查詢,并且介紹了如何依靠Github提供的GraphQL接口文檔來(lái)設(shè)計(jì)自己的GraphQL查詢語(yǔ)句,根據(jù)Github提供的GraphQL文檔,我們可以從Github獲取足夠復(fù)雜的數(shù)據(jù)。Github的API設(shè)計(jì)一直以來(lái)都是業(yè)界的標(biāo)桿,包括V3的Rest接口,以及V4的GraphQL接口,都值得仔細(xì)學(xué)習(xí)。