【译】更优秀的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…

相关文章
相关标签/搜索