[译] GraphQL - 学习 - 执行

秃头哥
• 阅读 2086

原文地址: Execution

在被验证之后,GraphQL 查询将由 GraphQL 服务器执行,该服务器返回的结果反映了请求查询的形状,通常是JSON。

GraphQL 不能在没有类型系统的情况下执行查询,让我们使用一个示例类型系统来演示执行查询。这是在这些文章的例子中使用的相同类型系统的一部分:

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

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

enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}

type Starship {
  name: String
}

为了描述在执行查询时发生的情况,我们使用一个例子来说明一下

{
  human(id: 1002) {
    name
    appearsIn
    starships {
      name
    }
  }
}

结果

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

您可以将 GraphQL 查询中的每个字段视为前面类型的函数或方法,返回下一个类型。事实上,这正是GraphQL 的工作方式。每个类型的字段都由一个名为 resolver 的函数来支持,该函数由 graphQL 服务器开发人员提供。当一个字段被执行时,将调用相应的解析器来生成下一个值

如果一个字段产生一个标量值,比如字符串或数字,那么执行就完成了。但是,如果一个字段产生一个对象值,那么查询将包含另一个应用于该对象的字段。这将一直持续到标量值到达为止。GraphQL 查询总是以标量值结束。

根字段 & 解析器 / Root fields & resolvers

在每个 GraphQL 服务器的顶层,都是一个类型,它表示所有可能的入口到 GraphQL API 中,它通常被称为根类型或查询类型。

在本例中,我们的查询类型提供了一个名为 human 的字段,该字段接受参数 id。该字段的解析器函数很可能访问一个数据库,然后构造并返回一个人工对象。

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

这个例子是用 JavaScript 编写的,但是 GraphQL 服务器可以用很多不同的语言来编写。解析器函数接收三个参数

  • obj 以前的对象,对于根查询类型的字段,通常不使用。
  • args 在 GraphQL 查询中提供给该字段的参数
  • context 一个为每个解析器提供的值,并保存重要的上下文信息,比如当前登录的用户,或者访问数据库。

匿名解析器 / Asynchronous resolvers

让我们仔细看看这个解析器函数中发生了什么。

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

context 用于提供对数据库的访问,该数据库用于通过在 GraphQL 查询中作为参数提供的 id 来为用户加载数据。因为从数据库中加载是异步操作,所以这返回了一个 Promise。在JavaScript 中,Promise 用于处理异步值,但在许多语言中都存在相同的概念,通常称为Futures, Tasks or Deferred。当数据库返回时,我们可以构造并返回一个新的 Human 对象。

注意,虽然解析器函数需要知道 Promise ,但是 GraphQL 查询没有。它只是希望 human 的字段能够返回一些东西,然后再问它的 name 。在执行期间,GraphQL 将等待 Promise, Future, 和 Tasks 在继续之前完成,并在最佳并发性的情况下完成

简单的解析器 / Trivial resolvers

既然已经有了一个 Human 对象,那么 GraphQL 执行就可以继续使用它所请求的字段

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

GraphQL 服务器由一个类型系统驱动,该系统用于确定下一步要做什么。甚至在人类 Human 返回任何东西之前,GraphQL 知道下一步将是解析 Human 类型的字段,因为类型系统告诉它,Human 字段将返回一个人。

在这种情况下,解析名称非常简单。名称解析器函数被调用,obj 参数是来自前一个字段的 new Human 对象。在这种情况下,我们期望 Human 对象拥有一个可以直接读取和返回的 name 属性。

事实上,许多 GraphQL 库会让你忽略这个简单的解析器,并假设如果一个解析器没有为字段提供,那么应该读取和返回相同名称的属性。

标量强制 / Scalar coercion

在解析 name 字段时,可以同时解析 appearsInstarships 字段。appearsIn 也可以有一个简单的解析器,如下:

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

注意,我们的类型系统声明将返回带有已知值的枚举值,但是这个函数返回的是数字!事实上,如果我们查看结果,就会看到适当的枚举值被返回。这是怎么运行的呢

这是标量强制的一个例子。类型系统知道预期输出的内容,并将解析器函数返回的值转换为维护 API 约定的值。在本例中,可能在我们的服务器上定义了一个枚举类型,该枚举在内部使用4、5和6等数字,但在 GraphQL 类型系统中表示为枚举值。

列表解析器 / List resolvers

我们已经看到,当一个字段返回了上面出现的 appearsIn 的列表时,会发生什么事情。它返回一个枚举值列表,因为这是类型系统所期望的,所以列表中的每个项都被强制到适当的 enum 值。当 starships 字段获取的时候会发生什么?

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

这个字段的解析器不仅仅是返回一个 Promise,它还在返回一个 Promise 列表。Human 对象有一列他们所引导的 Starships 的 id 列表,但是我们需要加载所有这些 id 来获取真正的 Starship 对象。

GraphQL 将会在继续之前等待所有这些 Promise,当留下一个对象列表时,它将并发地继续加载每个条目的 name 字段。

组合结果 / Producing the result

每个字段是解析完成后, 由此产生的值被存入一个 key-value 映射的字段名(或别名)作为键和解析值作为值, 继续从底部叶子字段查询备份到根元素的原始字段的查询类型。这些共同生成的结构反映了最初的查询,然后可以将其发送(通常是JSON)到请求它的客户端。

让我们最后看看原始查询,看看这些解析函数是如何产生结果的:

{
  human(id: 1002) {
    name
    appearsIn
    starships {
      name
    }
  }
}

查询结果

{
  "data": {
    "human": {
      "name": "Han Solo",
      "appearsIn": [
        "NEWHOPE",
        "EMPIRE",
        "JEDI"
      ],
      "starships": [
        {
          "name": "Millenium Falcon"
        },
        {
          "name": "Imperial shuttle"
        }
      ]
    }
  }
}
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
3年前
stardog graphql 简单操作
预备环境:下载stardog软件包graphql查询地址创建一个简单数据库./stardogadmindbcreatenstarwarsgraphql查询方式http地址:http://localhost:5820/starwars/graphql
九鹤 九鹤
4年前
GraphQL
GraphQL一种api文档查询语言基本语法本地运行javagitclonehttps://github.com/apollographql/starwarsservercdstarwarsservernpminstallnpmstart执行上面的命令之后打开下面的地址即可学习http://localhost:8080/grap
Wesley13 Wesley13
3年前
MySQL 的慢 SQL 怎么优化?
!(https://oscimg.oschina.net/oscnet/7b00ec583b5e42cc80e8c56c6556c082.jpg)Java技术栈www.javastack.cn关注阅读更多优质文章(https://www.oschina.net/action/GoToLink?urlhttp
Stella981 Stella981
3年前
2021年全球公有云终端用户支出将增长18% ;EMNLP 2020最佳论文:无声语音的数字发声
!(https://static001.geekbang.org/infoq/af/af9f6637b50b09be60b00a42f3812d5e.png)开发者社区技术周刊又和大家见面
可莉 可莉
3年前
2021年全球公有云终端用户支出将增长18% ;EMNLP 2020最佳论文:无声语音的数字发声
!(https://static001.geekbang.org/infoq/af/af9f6637b50b09be60b00a42f3812d5e.png)开发者社区技术周刊又和大家见面
Stella981 Stella981
3年前
Dubbo爆出严重漏洞!可导致网站被控制、数据泄露!附解决方案
http://dy.163.com/v2/article/detail/F5FPIFRU0511Q1AF.html  !(http://dingyue.ws.126.net/2020/0216/125ec4c4p00q5rcrs0019d200ig009qg00ig009q.png)  来源:华为云  原文地址:https://w
Stella981 Stella981
3年前
Eureka Server 开启Spring Security Basic认证
!Desktop(https://uploadimages.jianshu.io/upload_images/98242475ce94f98ae00f42f.jpg?imageMogr2/autoorient/strip%7CimageView2/2/w/1240)文章共503字,阅读大约需要2分钟!概述
Stella981 Stella981
3年前
AI 科学家带你快速 Get 人工智能最热技术
!(https://pic3.zhimg.com/80/v2af9f6637b50b09be60b00a42f3812d5e_1440w.jpg)日前,京东智联云与贪心学院联合举办的人工智能前沿技
林十二 林十二
1年前
vscode GraphQL插件踩坑
TLDRvscode的GraphQL语法插件,目前比较推荐GraphqlFoundation的GraphQL:LanguageFeatureSupport相关配置,见配置文件的语法规则,参考背景之前用的GraphQL插件,只开启了语法高亮.自己写的sche
美凌格栋栋酱 美凌格栋栋酱
5个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(