使用 GraphQL 触发嵌套解析器 [英] Trigger nested resolvers with GraphQL

查看:17
本文介绍了使用 GraphQL 触发嵌套解析器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想我在 GraphQL 解析器的工作方式中遗漏了一些明显的东西.这是我的架构(一个可以有AdditionalInformationPlace)的简化示例:

I think I'm missing something obvious in the way GraphQL resolvers work. This is a simplified example of my schema (a Place that can have 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:

I always get null as the additionalInformation:

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

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

It's my first GraphQL app, and I still don't get why the AdditionalInformation resolver is not automatically executed. Is there some way to let GraphQL know it has to fire it?

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

I've found this workaround but I find it a bit tricky:

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

推荐答案

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

Let's assume for a moment that additionalInformation was a Scalar, and not an Object type:

type Place {
  name: String!
  additionalInformation: String
}

places 解析器返回的值是:

[{name: 'Barcelona'}]

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

If you were to make a similar query...

query {
  places {
    name
    additionalInformation
  }
}

您希望 additionalInformation 是什么?它的值将为 null,因为 places 解析器返回的 Place 对象上没有 additionalInformation 属性.

What would you expect additionalInformation to be? It's value will be null because there is no additionalInformation property on the Place object returned by the places resolver.

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

Even if we make additionalInformation an Object type (like AdditionalInformation), the result is the same -- the additionalInformation field will resolve to null. That's because the default resolver (the one used when you don't specify a resolver function for a field) simply looks for a property with the same name as the field on the parent object. If it fails to find that property, it returns null.

您可能已经为 AdditionalInformation (foo) 上的一个字段指定了一个解析器,但是这个解析器永远不会被触发,因为不需要——整个 additionalInformation 字段为空,因此将跳过关联类型的任何字段的所有解析器.

You may have specified a resolver for a field on AdditionalInformation (foo), but this resolver is never fired because there's no need -- the whole additionalInformation field is null so all of the resolvers for any fields of the associated type are skipped.

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

To understand why this is a desirable behavior, imagine a different schema:

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

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

type Query {
  articles: [Article!]!
}

我们有一个数据库,其中包含一个 articles 表和一个 images 表作为我们的数据层.一篇文章可能有也可能没有与之相关的图像.我的解析器可能如下所示:

We have a database with an articles table and an images table as our data layer. An article may or may not have an image associated with it. My resolvers might look like this:

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

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

Let's say our call getArticlesWithImages resolves to a single article with no image:

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

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

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

As a consumer of the API, I request:

query {
  articles {
    title
    content
    image
  }
}

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

The image field is optional. If I get back an article object with a null image field, I understand there was no associated image in the db. As a front end client, I know not to render any image.

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

What would happen if GraphQL returned a value for the image regardless? Obviously, our resolver would break, since it would not be passed any kind of parent value. Moreover, however, as a consumer of the API, I would have to now parse the contents of image and somehow determine whether an image was in fact associated with the article and I should do something with it.

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

As you already suggested, the solution here is to specify a resolver for additionalInfo. You can also simply return that value in your places resolver, i.e.:

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

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

In reality, if the shape of your schema aligns with the shape of your underlying data layer, it's unlikely you'll encounter this sort of issue when working with real data.

这篇关于使用 GraphQL 触发嵌套解析器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆