GraphQL简单学习-运行原理

一、简介

可以参考一下前两天写的文章:https://my.oschina.net/hanchao/blog/3014079

二、Execution

参考文章:https://graphql.org/learn/execution/

参考文章:http://graphql.cn/learn/execution/

这篇文档解释了GraphQL是如何根据定义的schema来完成数据的组装和聚合的,应该算是整个GraphQL架构中最核心的设计之一了,值得了解。

在必要的数据校验环境后,GraphQL服务端会根据每一个query的实际要求来剪裁恰如其分的数据结构以进行响应,通常来说是JSON格式。

GraphQL非常依赖其类型模型(type system),我们来看一个实际的例子来演示如何执行一个query。这个例子和文档中其他部分是一致的:

type Query {
  human(id: ID!): Human
}

type Human {
  name: String
  appearsIn: [Episode]
  starships: [Starship]
}

enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}

type Starship {
  name: String
}

为了了解query执行的细节,我们来看一个例子:

// 请求
{
  human(id: 1002) {
    name
    appearsIn
    starships {
      name
    }
  }
}

// 响应
{
  "data": {
    "human": {
      "name": "Han Solo",
      "appearsIn": [
        "NEWHOPE",
        "EMPIRE",
        "JEDI"
      ],
      "starships": [
        {
          "name": "Millenium Falcon"
        },
        {
          "name": "Imperial shuttle"
        }
      ]
    }
  }
}

你可以将在GraphQL查询中每一个字段(field)看做是前一种类型的函数或方法,它返回下一种类型。事实上,这就是GraphQL的工作原理

在GraphQL服务端,每一个类型的每一个字段背后都是靠一个被称为 resolver 的函数来提供数据(提供支持)的。每当一个字段被要求返回,与其对应的 resolver 函数就会被执行,并产生下一个值(进入新一轮resolver 执行)。

 

如果发现字段返回的是一个标量值,如字符串或数字,此时执行就算告一段落了。

然而如果该字段执行后返回的是一个对象值,那么执行器会继续试图获取该对象值包含的字段,一直到最终得到一个标量值为止。

Root fields & resolvers 

每个GraphQL服务的最顶层类型是一个包含一切的统一API入口类型,通常我们称之为 Roottype 或 Querytype 

在我们的例子中, Querytype 提供了一个字段叫 human ,它接受一个参数 id 。这个字段对应的 resolver 函数通过操作数据库并构建和返回 human 对象。

Query: {
  human(obj, args, context, info) {
    return context.db.loadHumanByID(args.id).then(
      userData => new Human(userData)
    )
  }
}

这个例子是用JavaScript写的,但GQL服务端可以用 多种语言 来实现。 resolver 函数接受4个参数:

  • obj: 前一个对象(译:触发该 resolver 的字段所在的对象),这个参数对最顶层字段没有意义(译:当然啊,不然嘞~)
  • args: 来自GraphQL query
  • context: 提供所有 resolver 依赖的资源,例如数据库连接对象,当前登录的用户信息等
  • info: 包含与当前查询相关的特定于字段的信息的值,以及模式细节,可以参考 graphqlobjecttype

Asynchronous resolvers

我们来近距离看一下这个 resolver 函数的细节:

human(obj, args, context, info) {
  return context.db.loadHumanByID(args.id).then(
    userData => new Human(userData)
  )
}

context 参数中包含了数据库连接对象,用于数据查询来得到GraphQL query中要求的 id 数据。查询数据库是一个异步调用,所以返回一个 promise 。 promise 是javascript中的异步调用概念,不过其他很多语言也有对应的实现方式,通常被称之为 futures , tasks 或者 Deferred 。当数据库操作返回后,我们就可以构建和返回一个新的 Human对象啦。

需要注意的是尽管 resolver 函数返回的是 promise ,但GraphQL query并不是异步的,它会期望 human 携带了所有要求返回的数据。在执行过程中,GQL会一直等到 PromiseFutures 或 Tasks 完结后才会继续并最大化保持并发度(译:这一点很重要)。

Trivial resolvers

现在我们已经得到了一个 Human 对象,接下来GraphQL执行器将继续处理其下的字段。

Human: {
  name(obj, args, context, info) {
    return obj.name
  }
}

GraphQL服务依靠类型系统来决定如何继续执行下去。甚至是在 human 返回任何结果之前,GraphQL就可以根据类型系统要求提供的 human 类型声明得到下一步应该处理的字段有哪些。

在这个例子里 name 字段的处理是非常简单明了的。传入 name resolver 函数的 obj 参数就是前一步返回的那个 new Human 对象。例子中我们期望得到的 human 对象包含name 字段,已经如愿以偿。

事实上,很多GraphQL类库都不需要你提供这种简单的 resolver ,它们会默认自动从 obj 中读取并返回对应名字的属性(译:默认解析器规则)。

Scalar coercion

当 name 字段被处理过后, appearsIn 和 starships 字段会被同时处理。 appearsIn 字段也有一个 trivial resolver ,我们来仔细看一下:

Human: {
  appearsIn(obj) {
    return obj.appearsIn // returns [ 4, 5, 6 ]
  }
}

注意,我们的类型系统声明 appearsIn 将返回一个枚举类型,然而这个函数却返回的是number数组!实际上,如果我们查看结果,我们将看到相应的Enum值被归还。发生了什么?

这就是一个 Scalar coercion 的例子。类型系统知道应该返回什么,并将解析器返回的数据转换成了API声明要求的类型。在这个例子中,在服务的其他位置应该存在一个枚举类型的定义来标识 4,5,6 对应的枚举值。

List resolvers

通过 appearsIn ,我们已经看到了当一个字段需要一个返回多条数据时的细节。它返回了一个枚举值数组,然后类型系统将每个值转换成了对应的枚举值。那 starships 字段解析的细节有是什么呢?

Human: {
  starships(obj, args, context, info) {
    return obj.starshipIDs.map(
      id => context.db.loadStarshipByID(id).then(
        shipData => new Starship(shipData)
      )
    )
  }
}

这个字段的 resolver 不只是返回一个 promise ,它返回了一个 promise 数组。 human 对象拥有一个 starships 的 id 数组,但我们需要加载所有这些id 关联的 starship 对象。

GraphQL会等待所有的 promise 并发的完成后才会继续,当所有 starship 对象都得到后,GQL会继续并发的尝试获取这些对象的 name 字段。

Producing the result

当所有字段都处理完毕后,结果值构建成一个从叶子节点到根节点全链路的键值对映射,键为字段名,值为 resolver 返回的结果,最终按照请求的结构返回给客户端对应的数据结构(JSON结构)。

 

 

参考博文:

https://blog.kazaff.me/2018/03/25/%E5%86%8D%E5%93%81GraphQL/

转载于:https://my.oschina.net/hanchao/blog/3017276

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值