首页 > 解决方案 > 使用 GraphQL 触发嵌套解析器

问题描述

我认为我在 GraphQL 解析器的工作方式中遗漏了一些明显的东西。这是我的架构的简化示例(Place可以有AdditionalInformation):

import { ApolloServer, gql } from 'apollo-server';

const typeDefs = gql`
  type Place {
    name: String!
    additionalInformation: AdditionalInformation
  }

  type AdditionalInformation {
    foo: String
  }

  type Query {
    places: [Place]
  }
`;

以及相关的解析器

const resolvers = {
  Query: {
    places: () => {
        return [{name: 'Barcelona'}];
    }
  },
  AdditionalInformation: {
    foo: () => 'bar'
  }
};

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

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

当我执行基本查询时:

{
  places {
    name,
    additionalInformation {
      foo
    }
  }
}

我总是null得到additionalInformation

{
  "data": {
    "places": [
      {
        "name": "Barcelona",
        "additionalInformation": null
      }
    ]
  }
}

这是我的第一个 GraphQL 应用程序,我仍然不明白为什么AdditionalInformation解析器没有自动执行。有什么方法可以让 GraphQL 知道它必须触发它吗?

我找到了这个解决方法,但我觉得它有点棘手:

Place: {
  additionalInformation: () => { return {}; }
}}

标签: graphql

解决方案


让我们暂时假设它additionalInformation是一个标量,而不是一个对象类型:

type Place {
  name: String!
  additionalInformation: String
}

places解析器返回的值为:

[{name: 'Barcelona'}]

如果您要进行类似的查询...

query {
  places {
    name
    additionalInformation
  }
}

你希望additionalInformation是什么?它的值将为空,因为解析器返回的对象上没有additionalInformation属性。Placeplaces

即使我们创建additionalInformation一个 Object 类型(如AdditionalInformation),结果也是一样的——该additionalInformation字段将解析为 null。这是因为默认解析器(当您没有为字段指定解析器函数时使用的解析器)只是查找与父对象上的字段同名的属性。如果找不到该属性,则返回 null。

AdditionalInformation您可能已经为( )上的字段指定了解析器foo,但该解析器永远不会被触发,因为没有必要 - 整个additionalInformation字段为空,因此关联类型的任何字段的所有解析器都被跳过。

要理解为什么这是一种理想的行为,请想象一个不同的模式:

type Article {
  title: String!
  content: String!
  image: Image
}

type Image {
  url: String!
  copyright: String!
}

type Query {
  articles: [Article!]!
}

我们有一个带有表的数据库articles和一个images表作为我们的数据层。一篇文章可能有也可能没有与之关联的图像。我的解析器可能如下所示:

const resolvers = {
  Query: {
    articles: () => db.getArticlesWithImages()
  }
  Image: {
    copyright: (image) => `©${image.year} ${image.author}`
  }
}

假设我们的调用getArticlesWithImages解析为一篇没有图片的文章:

[{ title: 'Foo', content: 'All about foos' }]

作为 API 的使用者,我要求:

query {
  articles {
    title
    content
    image
  }
}

image字段是可选的。如果我返回一个带有空image字段的文章对象,我知道数据库中没有关联的图像。作为前端客户端,我知道不要渲染任何图像。

如果 GraphQL 返回一个值,会发生image什么?显然,我们的解析器会中断,因为它不会传递任何类型的父值。此外,然而,作为 API 的使用者,我现在必须解析内容image并以某种方式确定图像是否实际上与文章相关联,我应该对它做些什么。

TLDR;

正如您已经建议的那样,这里的解决方案是为additionalInfo. 您也可以简单地在places解析器中返回该值,即:

return [{name: 'Barcelona', additionalInfo: {}}]

实际上,如果架构的形状与底层数据层的形状一致,那么在处理真实数据时就不太可能遇到此类问题。


推荐阅读