概述
记住我们的梦想查询?
query GetRecipeAndCookwareInformation {recipe(id: "rec3j49yFpY2uRNM1") {namedescriptioningredients {text}instructionscookware {namedescriptioncleaningInstructions}}}
这个查询需要改变
在本课中,我们将
什么是实体?
在我们的诗意图谱supergraph中已经有了一个entity。让我们转到Studio的supergraph页面,导航到Schema页面。
从左侧列表选择对象。看到Cookware类型旁边的绿色E标签了吗?E代表实体!
注意:我们在探索器的文档面板中看到了相同的标签,它出现在返回Cookware类型的任何字段旁边。
因为这个实体定义在kitchenware子图谱中,我们没有克隆或本地运行,所以没有真正看到模式文件背后的情况。但没关系,我们实际上并不需要:Studio帮助我们确定了哪些实体类型可用!
您仍然可以在这里找到模式文件(如果您感兴趣的话)。
使用实体,每个子图谱可以进行以下一个或两个操作:
- 贡献不同的字段到实体
- 引用一个实体,这意味着使用它作为另一个由子图谱中定义的字段的返回类型
在我们本课程中,我们将重点学习如何引用一个实体。如果您对深入了解实体和更具体的地域概念感兴趣,您可以查看《Voyage》系列。
引用实体
《Cookware》类型是一个实体;我们知道我们可以引用实体,这意味着使用它作为另一个字段的返回类型。这正是我们在Recipe.cookware字段中想要做的!我们来试试吧。
首先,请确保两个 rover dev
进程仍在运行,以及 recipes
子图。
接下来,让我们打开位于我们的 recipes
子图 中的 schema.graphql
文件。
滚动到底部找到 Recipe
类型,并在末尾添加新的 字段。
type Recipe {# ... other Recipe fields"List of cookware used in the recipe"cookware: [Cookware]}
保存您的更改!第一个 rover dev
进程正在监听该 schema.graphql
文件的更改;当它检测到更改时,它将重新组合本地的 超图。我们来查看主要的 rover dev
进程... 哦不!出错了!
✨ change detected in ./schema.graphql...🔃 updating the schema for the 'recipes' subgraph in the session🎶 composing supergraph with Federation v2.3.1💀 composition failed, killing the routererror[E029]: Encountered 1 build error while trying to build a supergraph.Caused by:UNKNOWN: [recipes] Unknown type CookwareThe subgraph schemas you provided are incompatible with each other. See https://apollo.graphql.net.cn/docs/federation/errors/ for more information on resolving build errors.
我们遇到了构建错误 "未知类型 Cookware"。
这很合理!我们还没有为 recipes
子图 定义 Cookware
类型,因此它还不明白我们指的什么。让我们先修复它。
在同一个模式文件中,紧接在 Recipe
类型下方,添加 Cookware
类型。具体来说,我们将添加一个 占位符 的 Cookware
实体,它包含 recipes
子图 需要引用的最小 字段。
为了定义占位符,我们将在类型定义后添加 @key
指令。这个指令用于定义实体的主键,它唯一地标识一个实体实例。(例如,让我们区分代表 “铸铁煎锅” 的对象和代表 “炒锅” 的对象!)
type Cookware @key {}
我们可以使用 Studio 找到厨具类型的主键。回到 Schema 页面,选择 Objects 列表,点击 Cookware
类型查看其详细信息。我们将在 Keys 部分看到列出的 name
字段。
现在让我们回到 schema.graphql
文件。键 @key
指令需要一个 fields
属性,该属性设置为它的主键(或主键!)。在这种情况下,我们将 fields
属性设置为 name
。此外,我们还将添加类型定义中的 name
字段,它返回一个可非空的 String
。
type Cookware @key(fields: "name") {name: String!}
最后,由于子图不向实体增加任何 字段(我们只是在实体上使用 name
字段进行引用,未向其添加字段!),我们需要再添加一个东西。在 @key
指令中,添加另一个 参数用于 resolvable
并将其设置为 false
。
type Cookware @key(fields: "name", resolvable: false) {name: String!}
这表明 recipes
子图 不会定义对 Cookware
实体 的引用 解析器。如果你对实体和更具体的聚合概念有兴趣深入了解,可以 查看 Voyage 系列!
让我们保存我们的更改,并再次检查 rover dev
输出。
✨ change detected in ./schema.graphql...🔃 updating the schema for the 'recipes' subgraph in the session🎶 composing supergraph with Federation v2.3.1✅ successfully composed after updating the 'recipes' subgraph
一切正常!我们的模式更改已成功与其他 子图 以及路由器一起综合,路由器正在顺畅运行。
所以...我们都完成了,对吧?🤔 让我们回到Sandbox标签页,该标签页连接到我们本地运行的路由器,https://127.0.0.1:4000,并尝试运行我们的查询。
query GetRecipeAndCookwareInformation {recipe(id: "rec3j49yFpY2uRNM1") {namedescriptioningredients {text}instructionscookware {namedescriptioncleaningInstructions}}}
我们有了菜谱的详细信息...但是向下滚动到我们的数据中的炊具
部分时,我们看到的是null
!
现在,可能根本就没有关于铸铁炒锅的信息。尝试直接使用炊具
字段来查询它。
query GetCookware {cookware(name: "cast iron skillet") {namedescriptioncleaningInstructions}}
看起来我们收到了数据!所以...问题在哪里?
解析炊具
字段
我们的查询可以解析特定菜谱的数据,但当它到达菜谱
类型的炊具
字段并尝试解析它时,它并不知道该怎么办!这是因为我们还没有为Recipe.cookware
字段编写解析器函数!如果没有解析器函数,当查询特定菜谱的炊具
数据时,recipes
子图不知道该返回什么数据——甚至不知道在哪里找到它。让我们解决这个问题!
在src/resolvers/Recipe.js
文件中,为炊具
字段添加以下解析器函数。
cookware(recipe, _, { dataSources }) {const cookwareNamesList = dataSources.recipesAPI.getRecipeCookware(recipe.id);if (!cookwareNamesList) return;return cookwareNamesList.map((c) => ({name: c,}));},
这里发生了什么事?让我们来分析一下。
首先,解析器的参数。我们正在使用第一个参数parent
(重命名为recipe
)和第三个参数contextValue
(解构以访问dataSources
)。
接下来,在函数体的内部,我们正在使用data source方法getRecipeCookware
来检索菜谱的炊具列表。这个方法已经被我们实现了。
在这种情况下,如果那个菜谱没有任何炊具列表(它可能根本不用它,或者数据缺失!),我们将提前返回。
否则,我们将遍历列表并对每个项目返回一个对象。这个对象被称为实体表示。这是路由器用来表示实体特定实例的方式。表示始终包含该typename
和特定实例的@key
字段。
- 该
__typename
字段:GraphQL的所有类型都自动存在这个字段。它总是返回其包含类型的名称,作为字符串。例如,Cookware.__typename
返回“Cookware”。 - 该
@key
字段:定义了实体的主键,它可以唯一地识别实体的一个实例。我们知道Cookware
类型的主键是其name
字段。
所以,回到Recipe.cookware
解算器的最后一行:
return cookwareNamesList.map((c) => ({name: c,}));
在这种情况下,我们省略了__typename
字段,因为GraphQL会自动处理返回该值。由于cookwareNamesList
只有一个String
列表,我们不能简单地返回这个列表,而是需要map
函数将列表中的每个项目转换成路由器所期望的格式。
使用本地路由器测试
好的,我们已经对模式更改进行了代码修改!让我们跳转到沙箱中去检查我们的本地路由器。我们应该会得到我们梦想查询的全部数据。
query GetRecipeAndCookwareInformation {recipe(id: "rec3j49yFpY2uRNM1") {namedescriptioningredients {text}instructionscookware {namedescriptioncleaningInstructions}}}
太棒了,我们已经得到了它!
实践
使用下面的不完整模式来回答下一个问题。
type Fruit {id: ID!name: StringsoldBy: Vendor}??? {vendorId: ID!}
主要收获
- 一个实体是一个对象类型,它具有字段,这些字段分布在多个子图中。子图可以向实体贡献字段,或者简单地引用它。
- 引用一个实体意味着将其用作子图中定义的一个字段的返回类型。要引用一个实体,子图必须包含包含实体的
@key
的实体定义的存根,该定义包括实体的主要字段和将resolvable属性设置为false
的属性集。 - 对模式所做的添加可以成功组合,但请不要忘记添加所有必要的代码来解析新的字段!
接下来
本地一切工作得很好!让我们将这些更改推送到GraphOS,并更新我们的supergraph的生产到最新状态。
分享你对这节课的问题和评论
本课程目前处于
您需要 GitHub 账户才能发布以下内容。您还没有吗? 请在我们的大洋论坛上发布。