面向未來的API —— GitHub GraphQL API 使用介紹

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
本文根據GitHub開發人員文檔,整理翻譯了GitHub GraphQL API的使用方法,你可以瞭解到GraphQL的基本概念、GitHub GraphQL API的使用,兩個實際的使用案例,以及使用Explorer查詢GitHub GraphQL API

今年5月22日,GitHub發文宣布,去年推出的GitHub GraphQL API已經正式可用(production-ready),並推薦整合商在GitHub App中使用最新版本的GraphQL API v4。

相信大家對GraphQL早已不陌生,這一Facebook推出的介面查詢語言,立志在簡潔性和擴充性方面超越REST,並且已經被應用在很多複雜的業務情境中。GitHub這樣描述他們為何對GraphQL青睞有加:

我們為API v4選擇GraphQL,是因為它為我們的整合商提供了顯著的靈活性。相比於REST API v3,它最強大的優勢在於,你能夠精確的定義所需要的資料,並且毫無冗餘。通過GraphQL,你只需要一次請求就能取到通過多個REST請求才能獲得的資料。

在GitHub的開發人員文檔中有較為完整的GraphQL API v4介紹,本文整理並翻譯了其中的部分內容,並按以下章節組織,希望能給入門GraphQL或有興趣從事GitHub App開發的同學以參考:

  • 概念解釋
  • 使用方法
  • 示範案例
  • 文檔指引

概念解釋

GitHub GraphQL API v4在架構上和概念上與GitHub REST API v3有很大不同,在GraphQL API v4的文檔中,你會遇到很多新的概念。

Schema

schema定義了GraphQL API的類型系統。它完整描述了用戶端可以訪問的所有資料(對象、成員變數、關係、任何類型)。用戶端的請求將根據schema進行校正和執行。用戶端可以通過“自省”(introspection)擷取關於schema的資訊。schema存放於GraphQL API伺服器。

Field

field是你可以從對象中擷取的資料單元。正如GraphQL官方文檔所說:“GraphQL查詢語言本質上就是從對象中選擇field”。

關於field,官方標準中還說:

所有的GraphQL操作必須指明到最底層的field,並且傳回值為標量,以確保響應結果的結構明白無誤

標量(scalar):基礎資料型別 (Elementary Data Type)

也就是說,如果你嘗試返回一個不是標量的field,schema校正將會拋出錯誤。你必須添加嵌套的內部field直至所有的field都返回標量。

Argument

argument是附加在特定field後面的一組索引值對。某些field會要求包含argument。mutation要求輸入一個object作為argument。

Implementation

GraphQL schema可以使用implement定義對象繼承於哪個介面。

下面是一個人為的schema樣本,定義了介面 X 和對象 Y :

interface X {  some_field: String!  other_field: String!}type Y implements X {  some_field: String!  other_field: String!  new_field: String!}

這表示對象Y除了添加了自己的field外,也要求有介面X的field/argument/return type.(!代表該field是必須的)

Connection

connection讓你能在同一個請求中查詢關聯的對象。通過connection,你只需要一個GraphQL請求就可以完成REST API中多個請求才能做的事。

為協助理解,可以想象這樣一張圖:很多點通過線串連。這些點就是node,這些線就是edge。connection定義node之間的關係。

Edge

edge表示node之間的connection。當你查詢一個connection時,你通過edge到達node。每個edgesfield都有一個nodefield和一個cursorfield。cursor是用來分頁的。

Node

node是對象的一個泛型。你可以直接查詢一個node,也可以通過connection擷取相關node。如果你指明的node不是返回標量,你必須在其中包含內部field直至所有的field都返回標量。

基本使用

發現GraphQL API

GraphQL是可自省的,也就是說你可以通過查詢一個GraphQL知道它自己的schema細節。

  • 查詢__schema以列出所有該schema中定義的類型,並擷取每一個的細節:
query {  __schema {    types {      name      kind      description      fields {        name      }    }  }}
  • 查詢__type以擷取任意類型的細節:
query {  __type(name: "Repository") {    name    kind    description    fields {      name    }  }}
提示:自省查詢可能是你在GraphQL中唯一的GET請求。不管是query還是mutation,如果你要傳遞請求體,GraphQL請求方式都應該是POST

GraphQL 授權

要與GraphQL伺服器通訊,你需要一個對應許可權的OAuth token。

通過命令列建立個人access token的步驟詳見這裡。你訪問所需的許可權具體由你請求哪些類型的資料決定。比如,選擇User許可權以擷取使用者資料。如果你需要擷取版本庫資訊,選擇合適的Repository許可權。

當某項資源需要特定許可權時,API會通知你的。

GraphQL 端點

REST API v3有多個端點,GraphQL API v4則只有一個端點:

https://api.github.com/graphql

不管你進行什麼操作,端點都是保持固定的。

與 GraphQL 通訊

在REST中,HTTP動詞決定執行何種操作。在GraphQL中,你需要提供一個JSON編碼的請求體以告知你要執行query還是mutation,所以HTTP動詞為POST。自省查詢是一個例外,它只是一個對端點的簡單的GET請求。

關於 query 和 mutation 操作

在GitHub GraphQL API中有兩種操作:query和mutation。將GraphQL類比為REST,query操作類似GET請求,mutation操作類似POST/PATCH/DELETE。mutation mame決定執行哪種改動。

query和mutation具有類似的形式,但有一些重要的不同。

關於 query

GraphQL query只會返回你指定的data。為建立一個query,你需要指定“fields within fields"(或稱嵌套內部field)直至你只返回標量。

query的結構類似:

query {  JSON objects to return}

關於 mutation

為建立一個mutation,你必須指定三樣東西:

  1. mutation name:你想要執行的修改類型
  2. input object:你想要傳遞給伺服器的資料,由input field組成。把它作為argument傳遞給mutation name
  3. payload object:你想要伺服器返回給你的資料,由return field組成。把它作為mutation name的body傳入

mutation的結構類似:

mutation {  mutationName(input: {MutationNameInput!}) {    MutationNamePayload}

此樣本中input object為MutationNameInput,payload object為MutationNamePayload.

使用 variables

variables使得query更動態更強大,同時他能簡化mutation input object的傳值。

以下是一個單值variables的樣本:

query($number_of_repos:Int!) {  viewer {    name     repositories(last: $number_of_repos) {       nodes {         name       }     }   }}variables {   "number_of_repos": 3}

使用variables分為三步:

  1. 在操作外通過一個variables對象定義變數:
variables {    "number_of_repos": 3 }

對象必須是有效JSON。此樣本中只有一個簡單的Int變數類型,但實際中你可能會定義更複雜的變數類型,比如input object。你也可以定義多個變數。

  1. 將變數作為argument傳入操作:
query($number_of_repos:Int!){

argument是一個索引值對,鍵是$開頭的變數名(比如$number_of_repos),值是類型(比如Int)。如果類型是必須的,添加!。如果你定義了多個變數,將它們以多參數的形式包括進來。

  1. 在操作中使用變數:
repositories(last: $number_of_repos) {

在此樣本中,我們使用變數來代替擷取版本庫的數量。在第2步中我們指定了類型,因為GraphQL強制使用強型別。

這一過程使得請求參數變得動態。現在我們可以簡單的在variables對象中改變值而保持請求的其它部分不變。

用變數作為argument使得你可以動態更新variables中的值但卻不用改變請求。

示範案例

query 樣本

讓我們來完成一個更複雜的query。

以下query尋找octocat/Hellow-World版本庫,找到最近關閉的20個issue,並返回每個issue的題目、URL、前5個標籤:

query {  repository(owner:"octocat", name:"Hello-World") {    issues(last:20, states:CLOSED) {      edges {        node {          title          url          labels(first:5) {            edges {              node {                name              }            }          }        }      }    }  }}

讓我們一行一行的來看各個部分:

query {

因為我們想要從伺服器讀取而不是修改資料,所以根操作為query。(如果不指定一個操作,預設為query)

repository(owner:"octocat", name:"Hello-World") {

為開始我們的query,我們希望找到repository對象。schema校正指示該對象需要owner和name參數

issues(last:20, states:CLOSED) {

為計算該版本庫的所有issue,我們請求issue對象。(我們可以請求某個repository中某個單獨的issue,但這要求我們知道我所需返回issue的序號,並作為argument提供。)

issue對象的一些細節:

    • 根據文檔,該物件類型為IssueConnection
    • schema校正指示該對象需要一個結果的last或first數值作為argument,所以我們提供20
    • 文檔還告訴我們該對象接受一個states argument,它是一個IssueState的枚舉類型,接受OPEN或CLOSED值。為了只尋找關閉的issue,我們給states鍵一個CLOSED值。
edges {

我們知道issues是一個connection,因為它的類型為IssueConnection。為擷取單個issue的資料,我們需要通過edges取得node。

node {

我們從edge的末端擷取node。IssueConnection的文檔指示IssueConnection類型末端的node是一個issue對象。

既然我們知道了我們要擷取一個Issue對象,我們可以尋找文檔並指定我們想要返回的field:

titleurllabels(first:5) {  edges {    node {      name    }  }}

我們指定Issue對象的title,url,labels。

labels field類型為LabelConnection。和issue對象一樣,由於labels是一個connection,我們必須遍曆它的edge以到達串連的node:label對象。在node上,我們可以指定我們想要返回的label對象field,在此例中為name。

你可能注意到了在這個Octocat的公開版本庫Hellow-World中運行這個query不會返回很多label。試著在你自己的有label的版本庫中運行它,你就會看到差別了。

mutation 樣本

mutation往往需要你先通過執行query擷取請求資訊。本樣本有兩個操作:

  1. 通過query擷取issue ID
  2. 通過mutation給issue添加一個emoji表情
query FindIssueID {  repository(owner:"octocat", name:"Hello-World") {    issue(number:349) {      id    }  }}mutation AddReactionToIssue {  addReaction(input:{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}) {    reaction {      content    }    subject {      id    }  }}
不可能在執行一個query的同時執行一個mutation,反之亦然。

讓我們看一遍這個樣本。目標看起來很簡單:給一個issue添加一個emoji表情。

那麼我們怎麼知道首先需要一個query的?我們目前還不知道。

因為我們想要改動伺服器上的資料(給issue添加一個emoji),我們首先從schema中尋找有用的mutation。文檔顯示有addReaction這一mutation,描述為: Adds a reaction to a subject. ,很好!

該mutation的文檔列出了三個input field:

  • clientMutationId (String)
  • subjectId (ID!)
  • content (ReactionContent!)

!表明subjectId和content是必需的。content是必需的很好理解:我們要添加表情,肯定要指明使用哪個emoji。

但是為什麼subjectId也是必需的?因為subjectId是標明要給哪個版本庫中的哪個issue添加表情的唯一方式。

這就是為什麼在樣本中首先要有一個query:為的是擷取ID。

讓我們一行一行的來看這個query:

query FindIssueID {

這裡我們執行一個query,我們將它命名為 FindIssueID。給query命名是非必需的。

repository(owner:"octocat", name:"Hello-World") {

我們通過查詢 repository 對象並傳入 owner 和 name 兩個argument來指定版本庫。

issue(number:349) {

我們通過查詢 issue 對象並傳入 numberargument來指定所要添加表情的issue。

id

這就是我們從 https://github.com/octocat/Hello-World/issues/349擷取並作為subjectId的傳遞的id。

當我們執行這個query,就能得到 id: MDU6SXNzdWUyMzEzOTE1NTE=

注意:從query中返回的id就是我們將要在mutation中作為subjectID傳遞的值。文檔和schema自省都不會指明這個關係;你需要明白這些名稱背後的概念才能弄清楚。

知道了ID,我們就可以進行mutation了:

mutation AddReactionToIssue {

這裡我們執行一個mutation,並將它命名為 AddReactionToIssue。和query一樣,為mutation起名也是非必需的。

addReaction(input:{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}) {

讓我們來看這一行

    • addReaction 是mutation的名稱。
    • input 是所需的argument鍵. 對於mutation來說這個鍵總是 input 。
    • {subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY} 是所需的argument值。這個值總是由input field(此例中為subjectId 和 content )組成的input object(所以要加大括弧)。

我們怎麼知道content中用哪個值? addReaction 文檔告訴我們contentfield類型為ReactionContent,它是一個枚舉類型,因為GitHub的issue只支援部分emoji表情。文檔列出了允許的值(注意部分值與對應的emoji名稱不同):

    • THUMBS_UP
    • THUMBS_DOWN
    • LAUGH
    • HOORAY
    • CONFUSED
    • HEART

請求中剩下的部分由payload對象組成。在這裡我們指明當執行mutation之後我們希望伺服器返回的資料。這些行是從addReaction文檔中擷取的,可返回三個field:

    • clientMutationId (String)
    • reaction (Reaction!)
    • subject (Reactable!)

在此樣本中,我們返回兩個必須的field(reaction和subject),他們都有必需的內部field(分別是content和id)。

當我們執行mutation,返回結果如下:

{  "data": {    "addReaction": {      "reaction": {        "content": "HOORAY"      },      "subject": {        "id": "MDU6SXNzdWUyMTc5NTQ0OTc="      }    }  }}

最後還有一點:當你在input object中傳遞mutation field時,文法可能會很呆板。將field放到variable中可以改善這一點。以下是如何用variable重寫原來的mutation:

mutation($myVar:AddReactionInput!) {  addReaction(input:$myVar) {    reaction {      content    }    subject {      id    }  }}variables {  "myVar": {    "subjectId":"MDU6SXNzdWUyMTc5NTQ0OTc=",    "content":"HOORAY"  }}
你可能注意到了在之前的例子中的content(被直接在mutation中使用)沒有在HOORAY外面加引號,但在variable中使用時則有引號。原因如下:
  • 當在mutation中直接使用content時,schema希望的實值型別是ReactionContent,它是枚舉類型而不是字串。如果你加了引號在枚舉類型外面的話,schema會拋出錯誤,因為引號代表了字串。
  • 當在variable中直接使用content時,variables內容必須是有效JSON,所以引號是必須的。當在執行過程中variable被傳入mutation時,schema校正可以正確地解釋為 ReactionContent 類型。

文檔指引

在GitHub開發人員網站中,有網頁版的介面文檔以供開發人員查詢,但我不推薦大家通過這種方式查詢介面。

傳統的REST API作為伺服器資源或資料庫資料的映射,其結構可理解為一種列表的形式,故通過文檔目錄可方便的進行查詢。GraphQL API從名字就可以看出其內部採用的是一種類似“圖”的資料結構,以反映紛繁複雜的節點之間的關係。故很難通過從頭至尾閱讀文檔的方式全面的瞭解介面。

GitHub為開發人員提供了一個名為Explorer的工具:

通過開發人員自己的GitHub帳號授權,你可以使用這個工具方便的測試GitHub GraphQL API的各種請求。同時右側的側邊欄可以讓你方便的搜尋或連結到所需的API文檔。通過這種實驗和文檔相結合的方式,你就可以按照GraphQL的思維方式查看各個對象的串連關係和查詢要求了。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.