《GraphQL 名詞 101:解析 GraphQL 的查詢語法》【譯】

The Anatomy of a GraphQL Query

《GraphQL 名詞 101:解析 GraphQL 的查詢語法》【譯】

GraphQL 日漸成爲數據查詢的主流標準之一,整個生態圈也蓬勃發展。本文則由淺入深地詳細介紹基礎的 GraphQL 格式與關鍵字,有助於初學者對於 GraphQL 的使用形成體系認知。

GraphQL 日漸成爲數據查詢的主流標準之一。每天都會產生許多圍繞這項技術發展的精彩討論和新工具。GraphQL最棒的特性就是提供了一個豐富語言集來描述獲取數據的API。但是用戶該如何描述這種查詢語言,以及GraphQL這項核心技術本身呢?let’s talk!

GraphQL specification解釋了幾乎所有出現在GraphQL查詢語言中的概念,但是這篇文檔實在是太長了,所以我準備在這篇博客中,藉助一些具體的栗子來闡述其中一些最重要的概念,來幫助你成爲GraphQL專家!至少在紙上談兵方面 : )

注! 這篇文章可不是GraphQL的入門讀物。首先,你應該通讀concepts on the graphql.org docs,然後通過Learn Apollo tutorial來學習使用GraphQL,最後當你想繼續深入瞭解這項技術時,再回到這裏來吧!

最基本的GraphQL查詢

大家通常會使用“查詢”來稱呼 GraphQL API 服務的一切。但是這樣稱呼會有太多東西混雜在一起了。我們可能會把我們跪求服務端的一系列行爲稱爲一次查詢、一次修改或者一次訂閱,但我想“請求(request)”這個詞可能更加複合HTTP通信的概念,下面我們先來定義一些最基礎的概念:

  • GraphQL 請求體: 使用GraphQL語言定義的一個或多個操作或者數據片段,類型是字符串。
  • 操作: 可以被GraphQL執行引擎理解的一次查詢、修改或訂閱。

爲了搞清楚GraphQL各種基本操作之間的區別,讓我們先來看一個簡單的GraphQL請求體:

GraphQL document

A simple query and its parts.

這個請求體顯示了GraphQL的主要構建塊,它指定了你嘗試獲取的數據。

  • 字段(Fields):客戶端請求的數據單元,最後作爲JSON響應數據中的一個字段。請注意,它們始終稱爲“字段”,無論它們所在的層次有多深。在你的查詢中,對根節點字段的處理和最底層字段應該是一樣的。
  • 參數(Arguments):一組與特定字段關聯的鍵值對。這些參數會跟它們相關的字段一起被傳遞到服務器端執行,並影響服務器對字段的處理方式。如上面的示例,參數可以是字面量,接下來還有參數作爲變量形式的栗子。請注意,參數可以顯示在任何字段中,即使是嵌套層次很深的字段。

爲了讓你以非常簡潔的形式定義一個GraphQL查詢,上面的栗子是GraphQL的一種非常簡單的形式。但是在GraphQL操作中三種可選的部分都沒有在上述栗子中使用。如果你不僅僅是用GraphQL執行查詢操作,或是希望傳遞動態變量到GraphQL查詢中,你就需要利用到這些新的GraphQL特性。

這裏恰好有一個包含了所有可選部分的栗子:

A more detailed query and its parts.

A more detailed query and its parts.

  • 操作類型(Operation type):共三種類型:查詢(query)、更新(mutation)、訂閱(subscription)。它描述了你試圖進行何種操作。然而這些看起來意思很接近的操作,GraphQL服務器處理它們時還是會有一些不同。
  • 操作名稱(Operation name):爲了方便調試和服務端打日誌,最好給你的查詢賦予語義化的命名。這樣,無論你是在網絡日誌中或者GraphQL服務器上發現錯誤,你都可以通過名字很輕鬆的在代碼庫中定位問題,而不是靠猜測(類似的工具有 Apollo Optics)。可以把操作名稱想象成你最喜歡的編程語言中,一個語義化的函數名。
  • 變量定義(Variable definitions):當客戶端向GraphQL服務器發送查詢時,會存在查詢文檔不變,當某些字段會動態變化的情況。這些就是查詢中的變量。因爲GraphQL是靜態類型的,它可以實時驗證你是否傳遞了正確的變量。這正是你聲明變量類型時所計劃提供的能力。

變量使用特定的序列化協議(在目前的 GraphQL 服務實現中,通常是使用JSON )通過查詢文檔獨立傳輸。下面是一個變量對象在查詢文檔中的示例:
An example variables object.

An example variables object.

可以看到,這裏的關鍵是變量名稱需要與變量定義所匹配,其名稱是Episode枚舉中的一個成員。

  • 變量(Variables): 它是傳遞給GraphQL operation的值的字典,提供了operation的動態入參。

這裏有一個在談及Graph的技術意義時很重要,卻不常被提及的核心概念——花括號之間的所有東西叫什麼?

選擇集(selection set)是一個會在GraphQL 文檔中經常出現的概念,它賦予了GraphQL遞歸的特性,允許你獲取嵌套形式的數據。

  • 選擇集(selection set):它是一次operation中需要的一組字段,或者被嵌套在其他的字段中。GraphQL查詢必須包含一個標識選擇集的字段,且該字段返回的是對象類型,選擇集不能設置在返回值是標量類型(Scalar Types)的字段上,例如Int或者String

片段(Fragments)

當開始介紹片段(fragments)之後,GraphQL 將變得更加強大。它帶來了一系列新的概念。

  • 片段定義(Fragment definition):定義一個片段是GraphQL文檔的一部分。爲了區別於我們下面會介紹的內聯片段,它有時候也被稱爲片段命名

Fragments

A fragment definition and its parts.

  • 片段名稱(Fragment name): 片段(fragments )名在GraphQL文檔中必須是唯一的。這個名稱用於在操作(operations)或者其他片段(fragments )引用片段(fragments )。就像操作(operations)名稱一樣,片段名也能用於服務端日誌調試,所以我們推薦使用明確且有意義的片段(fragments )名。如果你使用了正確的片段(fragments )名,在優化數據獲取時,你能夠很好的追蹤你的代碼。
  • 類型條件(Type condition): GraphQL操作總是開始於查詢、修改或者訂閱schema中的類型,但是片段(fragments )能夠用於任一選擇,所以爲了將校驗片段(fragments )與校驗schema獨立開,你需要指定片段(fragments )能夠使用的類型,而這就是類型條件(Type condition)的作用。

就像操作(operations)一樣,片段也選擇集,使用起來也跟在操作(operations)中使用選擇集一樣。

在你的操作(operations)中使用片段(fragments )

片段(fragments )只有在操作(operations)中使用才能發揮出作用。片段是 GraphQL 的主要組合數據結構,通過片段可以重用重複的字段選擇,減少 query 中的重複內容。接下來我們將介紹使用片段(fragments )的兩種方式:
fragments

  • 片段擴展運算符(Fragment spread): 當你在操作或者其他片段中使用片段時,你可以將片段名置於...之後來表示片段。例如沒有片段時需要這樣編寫 query:
query noFragments {
  user(id: 4) {
    friends(first: 10) {
      id
      name
      profilePic(size: 50)
    }
    mutualFriends(first: 10) {
      id
      name
      profilePic(size: 50)
    }
  }
}

query 中存在下列重複的選擇集合:

{
  id
  name
  profilePic(size: 50)
}

可以用片段簡化爲:

query withFragments {
  user(id: 4) {
    friends(first: 10) {
      ...friendFields
    }
    mutualFriends(first: 10) {
      ...friendFields
    }
  }
}

fragment friendFields on User {
  id
  name
  profilePic(size: 50)
}

使用片段時需要加上 … 操作符表示展開片段內容,這稱爲片段擴展運算符(fragment spread),它可以用在任何選擇集(selection set)中,用以匹配片段的類型條件。

  • 內聯片段(Inline fragment): 如果你僅僅是想執行一些依賴結果類型的字段,卻不想把它們抽離成獨立的定義,你可以使用內聯片段( inline fragment)。它使用起來跟獨立的命名片段一樣,但是寫在查詢的內部。有一點不同的是,對於內聯片段來說類型條件(type condition)不是必須的,可以像使用指令一樣來使用它,接下來我們會演示指令(directive)的栗子。

指令(Directives)

指令是獨立於GraphQL server之外的一個附加功能。指令不會對結果的值產生影響,但是會影響哪些結果會被返回,也許還會影響這些結果是如何被執行的。指令可以出現在查詢的任何地方,但在這篇文章中我們只關注當前GrahpQL文檔所描述的skip(忽略)include(包括) 兩個指令。
Directives

You probably wouldn’t usually put all of these in one query, but it’s the easiest way to demonstrate.

你能在上文廚房水槽的栗子中使用指令skipincludeinclude 指令表示只有在 if 參數爲 true 時才引入片段表示的字段。skip 指令表示在 if 參數爲 true 時忽略片段中的字段。由於指令的語法相當靈活,我們可以利用它來給GraphQL添加更多的特性,而不是使用語法解析或者引入更復雜的工具的方式。

  • 指令(Directive): 在字段、片段或者查詢中的一個註釋,include 指令表示只有在 if 參數爲 true 時才引入片段表示的字段。skip 指令表示在 if 參數爲 true 時忽略片段中的字段。
  • 指令參數(Directive arguments): 與字段參數類似,只不過它們是被執行引擎處理,而不是傳遞給字段解析器(field resolver)。

總結

GraphQL 是在應用層對業務數據模型的抽象,是對數據請求定製的 DSQL,它解除了接口和數據之間的綁定,對業務數據結構做了抽象和整理,業務邏輯中的數據依賴於底層數據庫結構,並且可以由具體業務場景來定製,不同的業務場景只要基於同樣一套基礎業務數據模型就可以得到複用,在我看來,這纔是 GraphQL 帶來的最大改變和收益。
目前GitHub整站API已遷移GraphQL,淘寶也在生成環境有所實踐。

參考文檔:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章