【譯】更優秀的GraphQL官方中文文檔-客戶端如何使用

  • 原文檔地址:graphql.org/learn/
  • 文檔翻譯時間:2019年/3月/20日
  • 譯者:賀瑞豐(深度使用過GraqhQL)
  • 目的:提供更接地氣的中文說明,還有筆者本身的使用經驗 plus:其餘的翻譯太爛啦

簡介

  • 客戶端:GraphQL是一種查詢語言(for API),即發送Graphql請求
  • 服務器端:在執行時(runtime)使用你預先爲你的數據定義好的類型系統(Type System)來處理客戶端發送過來的Graphql請求。

它並無指定特定的數據庫或者存儲器,徹底靠你現有的代碼和數據支撐數據庫

創建一個GraphQL service 須要定義types和內部的fields,而後還須要一個解析函數來處理這些types.json

舉例:一個GraphQL service會以下所示 定義types 和 fields後端

//  who the logged in user is (me) 
type Query {
  me: User
}
// 針對User的查詢
type User {
  id: ID
  name: String
}
複製代碼

服務器端須要對每個Field解析數組

function Query_me(request) {
  return request.auth.user;
}

function User_name(user) {
  return user.getName();
}
複製代碼

一旦一個GraphQL Service 啓動了,就能夠接受GraphQL queries並檢驗和執行。先保證只處理預約好的types-fileds有關的query,而後執行解析函數並返回結果,示例以下bash

//發送以下query
{
  me {
    name
  }
}
//獲得以下json
{
  "me": {
    "name": "Luke Skywalker"
  }
}
複製代碼

Queries and Mutations(查詢和修改)

原文地址:graphql.org/learn/queri…服務器

Fields(字段)

最簡狀況下,Graphql會向對象上請求特定的字段,讓咱們從一個簡單的例子開始架構

  • Query
    {
      hero {
        name
      }
    }
    複製代碼
  • Result
    {
      "data": {
          "hero": {
              "name": "R2-D2"
          }
        }
      }
    複製代碼

你能夠當即發現上面的query和result的結構是一致的。這是GraqhQL的特性——覺得你老是獲取到你想要的,而且服務端精確地知道客戶端想要哪些字段。app

name 字段返回了一個 String類型,在上例中也就是星球大戰中的hero("R2-D2")異步

在上面的例子中,咱們僅僅是查詢了hero的名字並獲得了一個String,可是fields不只僅能夠是相似name這樣一個變量的形式,也能夠是一個Objects,這樣你就能夠對「hero」中的字段進行次級選擇(sub-selection ),GraphQL queries能夠遍歷相關的Objects-Fields,從而使客戶端能夠在一次請求中獲取大量的相關數據。每每咱們在REST架構下可能會作屢次的往返請求才能實現上述效果。下面是一個例子函數

  • Query

    {
      hero {
        name
        # Queries can have comments!
        # friends 就是上述所說的次級選擇,實現了相關查詢 🐂🍺
        friends {
          name
        }
      }
    }
    複製代碼
  • 返回結果

    {
      "data": {
        "hero": {
          "name": "R2-D2",
          "friends": [
            {
              "name": "Luke Skywalker"
            },
            {
              "name": "Han Solo"
            },
          ]
        }
      }
    }
    複製代碼

    注意,friends字段返回了一個數組。GraphQL queries會等同的對待單個的items或者列表,然而咱們能夠經過schema中推導出咱們指望返回是哪一種類型。

    Arguments(參數)

    若是僅僅是作能夠遍歷相關對象及其字段的話,GraqhQL已經在獲取數據上很是有用了。可是當你在查詢時能夠接受參數時,事情就會變得更加有趣了!

    在REST風格的系統中,你只能傳遞一個參數集合--也就是http請求中的查詢參數與URL段。可是在GraphQL中,每個字段或內嵌對象均可以獲取一個參數集,這讓GraphQL完全的取代了執行屢次API獲取這種方式。你甚至能夠向 scalar fields傳參,來實如今服務端一次性的數據轉換而不是分別在每一個客戶端作。

  • Query

    {
      human(id: "1000") {
        name
        # 這裏對高度作了傳參,要求單位是英尺
        height(unit: FOOT)
      }
    }
    複製代碼
  • 返回結果

    {
      "data": {
        "human": {
          "name": "Luke Skywalker",
          # 返回了英尺高度 🐂🍺
          "height": 5.6430448
        }
      }
    }
    複製代碼

參數能夠有不少類型。在上例中,咱們使用了一個枚舉類型(Enumeration type),即一個有限的選擇的集合(set),也就是 米 或者 英尺 等。 GraphQL默認內置了一個類型集, as long as they can be serialized into your transport format.

Read more about the GraphQL type system here.

Aliases(別名)

若是你敏銳的話,你可能已經發現了,既然返回的結果中的對象字段與query中的相關字段是匹配的可是並不包含參數,那麼你就不能夠針對同一個字段經過傳輸不一樣的參數來獲取不一樣的結果了。這就是咱們須要別名 aliases的緣由。

譯者注:當你須要針對某個字段經過不一樣的參數在一個Query中完成全部的數據獲取時,你就須要用到別名了

  • query
# 星球大戰北京。。。。 帝國 絕地武士,關注這裏的別名就好了
{
  empireHero: hero(episode: EMPIRE) {
    name
  }
  jediHero: hero(episode: JEDI) {
    name
  }
}
複製代碼
  • result
{
  "data": {
    "empireHero": {
      "name": "Luke Skywalker"
    },
    "jediHero": {
      "name": "R2-D2"
    }
  }
}
複製代碼

譯者注 :若是沒有別名,你不可能在一個request對hero這個字段獲取到兩種數據的,因此經過別名傳遞不一樣的參數來得到兩份數據,而且在返回的結果中也不是hero了,而是替換成了 empireHero 和 jediHero,這樣也方便了客戶端的處理。

Fragments(片斷):可複用單元

加入咱們App有一個複雜的頁面,在其中兩種hero的陣營分別佔據頁面的一邊,你能夠發現query一會兒就複雜起來了,由於咱們會重複的去聲明某些字段。

這也就是爲何GraphQL包含了 fragments 這種可複用, fragments 讓你能夠構建一個字段集而且在query中不停的複用。下面是例子

  • query
{
  leftComparison: hero(episode: EMPIRE) {
    ...comparisonFields
  }
  rightComparison: hero(episode: JEDI) {
    ...comparisonFields
  }
}

fragment comparisonFields on Character {
  name
  appearsIn
  friends {
    name
  }
}
複製代碼
  • result
{
  "data": {
    "leftComparison": {
      "name": "Luke Skywalker",
      "appearsIn": ["NEWHOPE", "EMPIRE","JEDI"],
      "friends": [
        {  "name": "Han Solo"},
        {  "name": "Leia Organa" },
      ]
    },
    "rightComparison": {
      "name": "R2-D2",
      "appearsIn": ["NEWHOPE","EMPIRE","JEDI"],
      "friends": [{  "name": "Luke Skywalker" } ]
    }
  }
}
複製代碼

你能夠發現若是沒有fragments 的話,那些字段會重複的出現。片斷的概念在將複雜的應用數據切分的時候常常被使用到,特別是當你有多個UI組件(有不一樣的片斷)在初始化數據獲取時

在片斷中使用變量

這裏用法太簡單了請翻看官方代碼:graphql.org/learn/queri…

Operation name(操做名)

到如今爲止,咱們已經使用過了query簡寫的語法糖,咱們省略了 query 關鍵字 和 query的名稱,可是在生產型的項目中指定query的名稱是頗有必要的。

下面是一個完整的例子:query 關鍵字做爲一個 **operation type **, 而後 HeroNameAndFriends 做爲操做名

query HeroNameAndFriends {
  hero {
    name
    friends {
      name
    }
  }
}
複製代碼

這種 operation type 既不是 query,mutationm 也不是 subscription, 它僅僅的描述了你想作哪一種類型的操做。這很是有必要,由於你使用省略語法糖的話你就不能給你的查詢操做傳遞變量了

譯者注:變量的定義都是放在了操做名以後的括號中的,若是省略了操做名,就不能傳變量給query了

給你的query命好名字,在 debug 和服務器打 log 的時候很是有用。舉個JavaScript的例子,若是你總是喜歡用匿名函數,那麼當這個函數報錯的時候,debug的時候就很難受。具名的query會讓你很是容易跟蹤

Variables(變量)

迄今爲止,咱們都是在query中將參數用字符串寫死的,可是在大多數狀況下,這些參數都是動態的。舉個例子:你可能會有一個下拉框來選擇一個 Star Wars episode ,並獲取到其值,傳遞給 query 做爲查詢參數。

!!請不要這樣作!!,直接在你的 query 代碼中 嵌入動態的變量,這樣雖然在 runtime 的時候變量會被解析成相應的字符串,而後被 serialize 轉化成 GraphQL-specific format。 !! 應該這樣 !!,GraphQL在處理外部傳輸過來的動態值的時候能夠像對待一等公民的處理方式,而且像一個單獨的 dictionary.

要使用變量,須要下面三個步驟

  • 將 query 中的靜態值替換成 $variableName
  • 在 query 操做名以後的括號中聲名該變量
  • 給變量一個單獨的,大多數狀況下JSON格式的 變量表,還要指定變量的類型。
query HeroNameAndFriends($episode: Episode) {
  # episode參數接受一個$episode變量,而變量在query操做名中被提早定義好了,而且要指定$episode變量的類型
  # 本例中變量類型是 Episode
  hero(episode: $episode) {
    name
    friends {
      name
        }
      }
    }
// 變量
這裏就是爲何上面叫 dictionary的緣由,經過episode字段能夠來匹配
{
  "episode": "JEDI"
}
複製代碼

如今,咱們能夠動態的傳遞episode的值給query,而不是重複的定義多個靜態的query(譯者注:如何傳遞變量給query如今尚未涉及到),再次強調,上述的用法是官方推薦,不要在query中採用插值的方式來構建query

譯者注:好比我以前在JavaScript中常常這麼幹

const {episode} = outerParameter
  # 採用ES6 插值字符串的方式來構建
  hero(episode: `${episode}`) {
    name
    friends {
      name
        }
      }
    }
複製代碼

Directives (指令)

咱們在上面討論瞭如何使用變量來避免手動的採用字符串插值的方法構建動態的查詢。在參數中使用變量確實解決了一大類問題。然而咱們會須要動態的去調整咱們整個query的結構,一樣的使用變量的方式。 舉個例子:咱們的 UI 組件有一個彙總和細節兩個視圖,其中一個比另外一個包含的字段要多不少。

query Hero($episode: Episode, $withFriends: Boolean!) {
  hero(episode: $episode) {
    name
    friends @include(if: $withFriends) {
      name
    }
  }
}
# 變量
{
  "episode": "JEDI",
  "withFriends": false
}
# result
{
  {
  "data": {
    "hero": {
      "name": "R2-D2"
    }
  }
}
}
複製代碼

上述例子就是使用來了特性 directive

directive:能夠給附加個一個字段或者 fragment,服務端會根據傳遞的變量值來處理你的query。

在 GraphQL 標準的核心中寫入了兩種 directive,按照標準實現的後端服務中都應該支持這兩種 directive

  • @include(if: Boolean),只有傳遞true時,纔會返回對應的結果
  • @skip(if: Boolean) 傳遞true時,不會返回對應結果

譯者注:大多數狀況下,你並不會用到它,只有當你發現你的query構建的很是複雜時,記得過來看看上述指令是否能幫助到你。

Mutations(變動)

REST 中,一個會對服務器有反作用的請求,不會使用 GET 方法(HTTP Request Method). GraphQL要簡單點-技術上來講任何query均可以被時限爲數據寫入。然而任何對於數據的修改(新建,修改,刪除)按照慣例來講都經過 mutation來完成是頗有用的。

和 query 同樣,若是 mutaion 也返回一個對象,你也能夠獲取內部的字段,用法幾乎和query是同樣的。

mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
  createReview(episode: $ep, review: $review) {
    stars
    commentary
  }
}
# 變量
{
  "ep": "JEDI",
  "review": {
    "stars": 5,
    "commentary": "This is a great movie!"
  }
}
# 返回值
{
  "data": {
    "createReview": {
      "stars": 5,
      "commentary": "This is a great movie!"
    }
  }
}
複製代碼

你可能發現了,咱們給review傳遞的參數比較特殊,是一個object type,意味着它能夠接受一個對象,而不只僅是一個 scalar type

Multiple fields in mutations

query 和 mutation有一點特殊的不一樣。 query的字段是並行執行的,而mutation的字段是串行的,一個接一個有序的進行的。 這就意味着若是咱們在一個請求中發起了兩個 incrementCredits mutation的操做的話,第一個請求會首先被返回,確保咱們本身的應用對race condition的考慮。

譯者注:當你的業務比較複雜時,你的頁面可能有很複雜的異步請求,這個時候不只僅要考慮到客戶端請求的發出順序,還要考慮到服務器對每一個請求的返回順序。

Inline Fragments(內聯片斷)

相似於其餘類型系統,GraphQL schemas 也支持接口和集合類型

若是你想查詢的字段返回的是一個接口或者集合類型,那麼你就須要用到內斂片斷來獲取實際數據,以下例:

  • request
    query HeroForEpisode($ep: Episode!) {
      hero(episode: $ep) {
        name
        ... on Droid {
          primaryFunction
        }
        ... on Human {
          height
        }
      }
    }
    // 變量
    {
        "ep": "JEDI"
    }
    複製代碼
  • result
{
  "data": {
    "hero": {
      "name": "R2-D2",
      "primaryFunction": "Astromech"
    }
  }
}
複製代碼

在上述 query中, hero 的字段返回了一個 Character 類型,依賴於episode參數來返回 Human or Droid,若是沒有內聯片斷你就只能查詢Character類型內置好的字段,好比name

Meta fields

使用內聯片斷的時候,若是沒有 __typename這種元字段標明數據的來源,客戶端沒法分辨數據,看下面的例子你就明白了

  • query
    {
      search(text: "an") {
        __typename
        ... on Human {
          name
        }
        ... on Droid {
          name
        }
        ... on Starship {
          name
        }
      }
    }
    複製代碼
  • result
    {
      "data": {
        "search": [
          {
            "__typename": "Human",
            "name": "Han Solo"
          },
          {
            "__typename": "Human",
            "name": "Leia Organa"
          },
          {
            "__typename": "Starship",
            "name": "TIE Advanced x1"
          }
        ]
      }
    }
    複製代碼

譯者注:沒有標誌的話,那麼返回的數據是下面這樣的?你徹底無法處理

{
  "data": {
    "search": [
      {
        "name": "Han Solo"
      },
      {
        "name": "Leia Organa"
      },
      {
        "name": "TIE Advanced x1"
      }
    ]
  }
}
複製代碼

GraphQL 提供了一些元字段,參看下面的連接

!!! 全文完 !!!

文檔的Server端的翻譯也作好了:juejin.im/post/5c9330…

相關文章
相關標籤/搜索