8. 解析器
15m

概述

现在是时候引入那些让获取架构中数据成为可能的函数了。 在我们的架构中。

在本课中,我们将

  • 探索什么是 ,以及它们接受哪些参数
  • 学习如何从 函数访问

介绍解析器

一个 解析器 的使命是填充你架构中 的数据。

究竟什么是 ? 解析器是一个函数。它与它填充数据的 同名。它可以从任何 获取数据,然后将这些数据转换为客户端所需的形式。

Hand-drawn illustration depicting a resolver function retrieving data from data-land

src 目录中,我们将从创建一个新的 resolvers.ts 文件开始。

📂 src
┣ 📂 datasources
┣ 📄 graphql.d.ts
┣ 📄 helpers.ts
┣ 📄 index.ts
┣ 📄 resolvers.ts
┗ 📄 schema.graphql

在这个文件中,我们将声明一个 resolvers 常量,暂时将其分配给一个空对象。让我们导出它,因为我们会在我们的服务器配置选项中需要它。

src/resolvers.ts
export const resolvers = {};

我们的 resolvers 对象的键将对应于我们架构的类型和

要为 创建一个 ,我们首先需要在 resolvers 对象中添加一个 Query 键。该键的值将是 另一个 包含 featuredListings 键的对象。

resolvers.ts
export const resolvers = {
Query: {
featuredListings: () => {},
},
};

注意我们的 对象是如何遵循我们架构的结构的? featuredListingsQuery 类型上的一个 ,所以我们将其定义为 Query 键上的一个属性。

解析器的形状

我们的 将如何与我们的 交互? 这就是解析器参数发挥作用的地方。解析器函数具有一个特定的签名,包含四个可选参数: parentargscontextValue 以及 info

Hand-drawn illustration depicting a resolver function signature with its four parameters
featuredListings: (parent, args, contextValue, info) => {},

让我们简单地回顾一下每个参数,以了解它们各自的职责

  • parent
    parent 是此 父级 的返回值。在处理解析器链时,这将非常有用。
  • args
    args 是一个对象,其中包含所有由 GraphQL 操作为 提供的 。当 特定项目(例如特定列表而不是 所有 列表)时,在客户端中,我们将使用 ,并附带一个 id ,该参数将在服务器端通过此 args 参数访问。
  • contextValue
    contextValue 是一个对象,在为特定操作执行的所有 中共享。解析器需要此 来共享状态,例如身份验证信息、数据库连接,或者在我们的例子中,共享 RESTDataSource
  • info
    info 包含有关操作执行状态的信息,包括 名称、从根节点到字段的路径等等。它不像其他参数那样频繁使用,但在执行更高级的操作(例如在 级别设置缓存策略)时,它会非常有用。

在本课程中,我们将探索前三个参数,首先从 contextValue 开始,它是第三个位置参数。

在解析器中访问数据源

如上所述, contextValue 是一个在所有 中共享的对象。解析器就是通过它来访问相同的共享状态,例如

让我们通过在 featuredListings 中添加参数来实际操作一下。

resolvers.ts
featuredListings: (parent, args, contextValue, info) => {},

这里参数的顺序很重要。如果我们只添加了一个参数,那么函数会将其视为第一个可选参数: parent。我们需要 contextValue,它是第三个参数,才能访问我们的

我们 不需要 前两个参数,所以按照惯例,我们将它们命名为带下划线的名称:一个下划线表示第一个 (parent) 以及两个下划线表示第二个 (args)。对于 contextValue,我们将对其进行解构,以访问其子对象 dataSources。并且我们可以省略第四个参数 info,因为我们不会使用它。

resolvers.ts
featuredListings: (_, __, { dataSources }) => {},

dataSources 属性是 ListingAPI 类实例所在的地方。

访问 ListingAPI

我们将把 ListingAPI 类连接到我们的 在下一课中。现在,我们将假设 dataSources 将包含一个名为 listingAPI 的属性(按照惯例用小写字母编写,因为它是一个 ListingAPI 类的实例)。我们可以在这里调用该实例的 getFeaturedListings 方法,直接在我们的 中——该方法将完成其余工作!

resolvers.ts
Query: {
featuredListings: (_, __, { dataSources }) => {
return dataSources.listingAPI.getFeaturedListings();
},
}

提示: 作为最佳实践,在处理您的 时,尽量使解析器函数尽可能集中。这样做使您的 API 对未来的更改更具弹性。您可以安全地重构数据获取代码,或将来源完全从 REST API 更改为数据库,而不会破坏您的 API。这还使您的解析器更易读,更容易理解,这在您定义越来越多的解析器时非常有用!

现在,您可能会在 IDE 中看到一些红色的波浪线错误。TypeScript 正在抱怨我们为 featuredListings 提供了许多参数,但我们没有指定它们是什么类型的数据。相反,我们看到几乎所有内容都被推断为 any 类型。

TypeScript 错误出现在我们的 IDE 中
Parameter '_' implicitly has an 'any' type.
Parameter '__' implicitly has an 'any' type.
Binding element 'dataSources' implicitly has an 'any' type.

让我们在下一课中找出如何修复这些类型错误。

练习

以下哪些关于解析器的说法是正确的?
contextValue 参数有什么用?

关键要点

  • 是可以为架构中的每个 定义的函数。它们接受四个可选参数,parentargscontextValueinfo,它们负责在查询特定 时返回数据。

接下来

我们的 函数仍然缺少一些东西:类型注释!这意味着我们在代码中看到一些 TypeScript 错误。我们可以手动编写这些类型,但有一种更有效的方法。在下一课中,我们将探讨如何从我们的 生成 TypeScript 类型。

上一个

分享您关于本课的问题和评论

本课程目前处于

测试版
.您的反馈有助于我们改进!如果您遇到困难或困惑,请告诉我们,我们将帮助您。所有评论都是公开的,必须遵循 Apollo 行为准则。请注意,已解决或已处理的评论可能会被删除。

您需要一个 GitHub 帐户才能在下面发布。没有? 改为在我们的 Odyssey 论坛中发布。