为什么 graphql-js 执行器在父解析器返回 null/undefined 时停止解析具有自己的解析器的子字段?



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


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 中,null 表示缺少值。如果字段解析为 null,则调用其"子"字段解析器是没有意义的,因为它们无论如何都不会在响应中返回。

从规范的"值完成"部分(强调我的):

  1. 如果字段类型为非空类型:
    a。让 innerType 成为 fieldType 的内部类型。
    二.让 completeResult 是调用 CompleteValue(innerType、fields、result、variableValues)的结果。
    三.如果 completeResult 为 null,则引发字段错误。
    d. 返回已完成结果。
  2. 如果结果为 null(或另一个类似于 null 的内部值,例如未定义或 NaN),则返回 null。
  3. 如果字段类型为列表类型:
    a。如果结果不是值的集合,则引发字段错误。
    二.让 innerType 成为 fieldType 的内部类型。
    c. 返回一个列表,其中每个列表项都是调用 CompleteValue(innerType、fields、resultItem、variableValues)的结果,其中 resultItem 是结果中的每个项。
  4. 如果 fieldType 是标量或枚举类型:
    a. 返回"强制"结果的结果,确保它是 fieldType 的合法值,否则为 null。
  5. 如果字段类型是对象、接口或联合类型:
    a。如果字段类型是对象类型。
    我。让对象类型为字段类型。
    b. 否则,如果字段类型是接口或联合类型。
    我。让 objectType 为 ResolveAbstractType(fieldType, result)。
    三.让子选择集是调用合并选择集(字段)的结果。
    d. 正常返回 ExecuteSelectionSet(subSelectionSet, objectType, result, variableValues) 的计算结果(允许并行化)。

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

最新更新