【问题标题】:Why graphql-js executor stops resolving child fields that have their own resolvers when the parent resolver return null/undefined?为什么当父解析器返回 null/undefined 时,graphql-js 执行器停止解析具有自己的解析器的子字段?
【发布时间】:2020-04-22 04:47:44
【问题描述】:

在用 JavaScript 为 GraphQL 编写库时,我偶然发现了一个奇怪的行为。我设法在一个非常简单的例子中隔离了它。让我们以这个服务器sn-p:


    const { ApolloServer, gql } = require("apollo-server")

    const typeDefs = gql`
      type Book {
        resolveItSelf: String
      }

      type Query {
        book: Book
      }
    `

    const resolvers = {
      Query: {
        book: () => {
          return null // same behavior with undefined here
        }
      },
      Book: {
        resolveItSelf: () => "resolveItSelf"
      }
    }

    const server = new ApolloServer({ typeDefs, resolvers })

    server.listen().then(({ url }) => {
      console.log(`????  Server ready at ${url}`)
    })

如果我们使用以下查询查询此服务器:

    {
      book {
        resolveItSelf   
      }
    }

我们得到这个结果:

{
  "data": {
    "book": null
  }
}

所以,我期待 graphql 执行器尝试解析“resolveItSelf”字段(它有自己的解析器),即使图书解析器返回 null。

获得我期望的行为的一种方法是稍微更改本书的解析器:

const resolvers = {
  Query: {
    book: () => {
      return {} // empty object instead of null/undefined
    }
  },
  Book: {
    resolveItSelf: () => "resolveItSelf"
  }
}

然后我们得到这个结果:

{
  "data": {
    "book": {
      "resolveItSelf": "resolveItSelf"
    }
  }
}

即使父项为空,该字段也已解析!

所以我的问题是,为什么如果父解析器返回 null/undefined,即使请求的字段可以自己解析,graphql-js 执行程序也会停止尝试解析字段? (草案中是否有涉及这一点的部分?)

【问题讨论】:

    标签: graphql graphql-js


    【解决方案1】:

    在 GraphQL 中,null 表示缺少值。如果一个字段解析为 null,则调用其“子”字段解析器没有意义,因为它们不会在响应中返回。

    来自spec(重点是我的)的“值完成”部分:

    1. 如果 fieldType 是非 Null 类型:
      一种。令 innerType 为 fieldType 的内部类型。
      湾。令 completedResult 为调用 CompleteValue(innerType, fields, result, variableValues) 的结果。
      C。如果 completedResult 为 null,则抛出字段错误。
      d。返回完成的结果。
    2. 如果结果为null(或其他类似于null的内部值,如undefined或NaN),则返回null。
    3. 如果 fieldType 是 List 类型:
      一种。如果结果不是值的集合,则抛出字段错误。
      湾。令 innerType 为 fieldType 的内部类型。
      C。返回一个列表,其中每个列表项是调用 CompleteValue(innerType, fields, resultItem, variableValues) 的结果,其中 resultItem 是结果中的每个项。
    4. 如果 fieldType 是标量或枚举类型:
      一种。返回“强制”结果的结果,确保它是 fieldType 的合法值,否则为 null。
    5. 如果 fieldType 是对象、接口或联合类型:
      一种。如果 fieldType 是 Object 类型。
      一世。让 objectType 为 fieldType。
      湾。否则,如果 fieldType 是 Interface 或 Union 类型。
      一世。设 objectType 为 ResolveAbstractType(fieldType, result)。
      C。让 subSelectionSet 成为调用 MergeSelectionSets(fields) 的结果。
      d。正常返回评估 ExecuteSelectionSet(subSelectionSet, objectType, result, variableValues) 的结果(允许并行化)。

    换句话说,即使一个字段的类型是一个对象(因此有一组也可能被解析的字段),如果它解析为 null,则不会沿着该路径进一步执行。

    【讨论】:

    • 然而在参考节点实现中,尽管true 不是对象,但父节点的resolve: () => true, 会触发子节点的resolve。父级仅将执行转发到子字段/类型解析器的一个可能用例是将查询分组,因此父类型仅成为命名空间。
    猜你喜欢
    • 1970-01-01
    • 2018-11-25
    • 2014-07-24
    • 1970-01-01
    • 2015-08-09
    • 1970-01-01
    • 1970-01-01
    • 2020-10-07
    • 2021-09-09
    相关资源
    最近更新 更多