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

相关文章
相关标签/搜索