【譯】更優秀的GraphQL中文文檔-服務器端

  • 文檔翻譯時間:2019年/3月/21日
  • 譯者:賀瑞豐(深度使用過GraqhQL)
  • 目的:提供更接地氣的中文說明 plus:其餘的翻譯太爛啦

!歡迎來信與譯者討論GraphQL相關問題 !node

Schemas and Types

本文中,你會學到 GraphQL 類型系統的全部細節而且它是如何去描述什麼樣的數據是能夠被查詢的。既然GraphQL能夠再任何後端框架和編程語言中使用,因此咱們暫且不談GraphQL的實現細節,只聚焦於核心概念。編程

Type System

若是你已經見過GraphQL query,那麼你就知道GraphQL 查詢語言基本上是在對象上查詢指定的字段。例如後端

  • query
    {
      hero {
        name
        appearsIn
      }
    }
    複製代碼
  • result
    {
      "data": {
        "hero": {
          "name": "R2-D2",
          "appearsIn": ["NEWHOPE","EMPIRE","JEDI"]
        }
      }
    }
    複製代碼
  1. 咱們從一個特殊的「根」對象開始
  2. hero 地段上作選擇
  3. 對於 hero 返回的對象,咱們選擇 nameappearsIn 字段

由於 GraphQL 的查詢與結果在結構形式上高度匹配,你就能夠預測服務端會返回什麼樣的數據而不用關心服務端具體是怎麼實現的。可是對咱們需求的數據作精確的描述是頗有用的--也決定了什麼樣的字段咱們能夠去查詢?哪一類對象會被返回?在子對象中哪些字段是可用的?這就是schema的做用。數組

每個 GraphQL services 都會定義一個 type 的集合,完整的描述了你能夠訪問的數據集合。而後,當接受到查詢時,請求基於 Schema 被檢驗、執行.緩存

Type language

GraphQL services 能夠被任何語言實現,既然咱們不依賴於一種特定的編程語言的語法,例如JavaScript來說解 GraphQL schemas ,咱們會來定義咱們本身的簡單語言--"GraphQL schema language",它和QL語言相似,讓咱們能夠和 GraphQL schemas 良好的溝通。bash

Object types and fields

GraphQL schema 最基礎的組件是 object types,它標識了你能夠從後端服務中獲取哪些對象和子字段。例如:服務器

type Character {
  name: String!
  appearsIn: [Episode!]!
}
複製代碼

這種語言可讀性高,可是咱們仔細過一下細節,來保持基本的術語理解app

  • Character 是一個 GraphQL Object Type ,表示這個對象是擁有某些字段的,你的 schema 中大部分的types 都是 Object Type
  • nameappearsInCharacter類型,這意味着在 GraphQL query 在操做 ** Character type** 的時候只能使用 name 和 appearsIn。
  • String 是內置的 **scalar types **之一,這種類型resolve to a single scalar object,而且沒有次級選擇。咱們後面詳細討論
  • String! 說明該字段必填,也就是說在你發起GraphQL query時,該字段必須是有值的,在類型語言中,咱們用感嘆號來標識。
  • [Episode!]! 表明着 Episode objects的數組,而且是非空數組,並且請求 appearsIn 字段的時候必須傳一個數組,數據裏面每一個數據都必須是 Episode 類型的。

如今你知道了 GraphQL object type 是什麼樣子的,而且如何去閱讀這門語言。框架

Arguments

每個 GraphQL object type 均可以有參數,例如:編程語言

type Starship {
  id: ID!
  name: String!
  length(unit: LengthUnit = METER): Float
}
複製代碼

每一個參數都有名字。不像 JavaScript 和 Python 函數接收一個有序的參數集合,GrapphQL中的參數傳遞時都指定了名稱,在上例中,length 字段有一個定義好的參數 unit.

參數能夠是必須的也能夠是可選的,當參數是可選時,咱們提早定義一個默認值--若是 unit 參數沒有傳值,它會使用默認值 METER。

The Query and Mutation types

在你的 schema 中有兩種類型是很是特殊的

schema {
  query: Query
  mutation: Mutation
}
複製代碼

每個 GraphQL service 都有一個 query type,mutation type不必定都有,他們特殊在-定義了每個 GraphQl query 的入口 當你看到以下的query的時候

query {
  hero {
    name
  }
  droid(id: "2000") {
    name
  }
}
複製代碼

意味着 你的GraphQL service必須有一個 Query type 包含了 hero and droid 字段

type Query {
  hero(episode: Episode): Character
  droid(id: ID!): Droid
}
複製代碼

Mutations也是相似的方式,你在 Mutation type 上定義字段,這些字段就是你執行mutation時的入口。

要記住:Query、Mutation types除了能夠定義入口以外,和普通的 object type 沒有什麼區別。

Scalar types

一個 GraphQL object type 具備名字和字段,可是在某些時候,這些字段必須解析成某些具體數據。 例如 appearsIn 字段被返回 [ "NEWHOPE","EMPIRE","JEDI"]數組。

GraqhQL內置了一個 scalar type 的集合

  • Int :32位有符號整數
  • Float : 雙精度有符號浮點數
  • String : UTF-8 字符串
  • Boolean: true of false
  • ID: 表明一個特殊的標識,常常用於獲取某個特定的對象,或者做爲緩存中的 key 標識。ID 類型和 String 使用同樣的方式來 serialize;可是當咱們定義ID時並不要求其可讀性高

在大多數 GraphQL service 實現中,常常會有一個特殊的自定義的 scalar type.例如 咱們能夠定義一個 Date type:

scalar Date
複製代碼

而後咱們就個能夠本身來定義如何去serialize,deserialized 和 validate 這種類型了。例如,你能夠指定 Date type被 serialize成一個 整數時間戳。

Enumeration types

也被叫作Enums,這種類型是一種特殊的 scalar types,只能在特定的值的集合中選擇。這樣的做用是

  • 驗證這種類型的參數只能是特定的某幾個值
  • 整個類型系統中,該字段始終只有有限的幾個值可選

下面是 一個枚舉定義在 GraphQL schema language 是什麼樣的?

enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}
複製代碼

這覺得這不管你在哪裏使用 type Episode,咱們認爲它只有上述的那幾個值。

注意:各類語言實現的 GraphQL service 都有本身處理枚舉類型的方式。在那種將enums視做一等公民的語言中,這種實現能夠利用上述特性。可是在像JavaScript這種對 enum 沒有支持多點語言中,其值可能被映射爲一系列的整數集合。而後客戶端是不會知道這些細節的,客戶端徹底依照enum值的字符串名稱來操做(譯者注:這裏我也沒太懂,等用了nodejs實現一遍,再回來補充)

Lists and Non-Null

這一段屬於廢話又多,知識點又能夠從前面推理獲得的。就不作翻譯了,其實能夠跳過這裏。若是有不放心的話能夠參看原文

Interfaces

和其餘類型系統同樣,GraphQL也支持 Interfaces. 一個 interface 是一個抽象的 type,它包含了一個肯定的字段集,當你作 type a inmlements b(interface)這樣的操做時,a類型就必須包含b接口中定義的字段。 咱們舉個例子來看一下

提早定義一個接口,等待其餘的 type 來 implement,

interface Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
}
複製代碼

任何implement Character的類型都必須有上述的字段和參數,以下所示

type Human implements Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
  # 上面是Character接口的
  starships: [Starship]
  totalCredits: Int
}

type Droid implements Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
  # 上面是Character接口的
  primaryFunction: String
}
複製代碼

除了Character 接口定義的字段外,Human 和 Droid還能夠有本身定義的字段。

接口在你想返回一個對象或者一系列object type的時候有用。

query HeroForEpisode($ep: Episode!) {
  hero(episode: $ep) {
    name
    ... on Droid {
      primaryFunction
    }
  }
}
// 變量
{
  "ep": "JEDI"
}
複製代碼

hero字段提早定義好了返回一個 Character type,並且在後端實現中只有Human或者Droid implement 了 Character type,因此對於Character type內置好了的字段-好比name,你能夠直接獲取,可是其餘實現層特定的字段好比 Droid 上的primaryFunction就必須採用內聯片斷來獲取了

Learn more about this in the inline fragments section in the query guide.

譯者注:接口是用來作抽象的 type,全部對接口的實現的 type 都具備公共字段

Union types

Union type 和接口類型很是類似,可是並無指定type之間的公共字段

union SearchResult = Human | Droid | Starship
複製代碼

不管何時咱們 return SearchResult type,咱們能夠得到上述的三個 type.注意union type必須有實際的 object type組成,不能由其餘的union type 或者接口組成。

在本例中,若是你查詢的字段返回的是 SerchResult union type ,也須要用到內聯片斷

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

__typename 字段返回 String ,做爲一個標識符來與其餘的data type作區分。

而且,本例中,既然 HumanDroid 都有一個共同的接口(Character),你能夠直接查詢他們的公共字段而不是重複的在其中查詢。

{
  search(text: "an") {
    __typename
    ... on Character {
      name
    }
    ... on Human {
      height
    }
    ... on Droid {
      primaryFunction
    }
    ... on Starship {
      name
      length
    }
  }
}
複製代碼

這裏注意 nameStarship中仍然要被指定,不然查詢結果中不會出現 **name **,由於它並不享有一樣的接口。

Input types

目前爲止,咱們只討論過傳遞 scalar values,好比 enums 或者 strings 做爲參數。可是你能夠傳遞複雜的對象做爲參數。這在 mutation 中很是有用,你嚐嚐會傳遞一個大的對象給服務器。在 GraphQL schema language 中,input type 和其餘的常規的 object types同樣,可是不是用 type 關鍵字了,而是用 input 做爲關鍵字。

input ReviewInput {
  stars: Int!
  commentary: String
}
複製代碼

下面是一個使用示例

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!"
    }
  }
}
複製代碼

input object type 上的字段也能夠指向其餘的 input object type (譯者注:對象嵌套來組合成更加複雜的參數結構),可是不能把 input / output type搞混了。input object type在字段上是不能支持參數傳遞的。

!!!全文完 !!! 都看到這裏啦,翻譯不易,請留下您的👍吧!這會成爲我持續提供優質文章的動力

客戶端的文檔也翻譯好了:juejin.im/post/5c9247…

相關文章
相關標籤/搜索