11. 查询参数
10m

概述

我们现在可以 来自我们 REST API 的数据,但只能询问 featuredListings 。让我们给我们的 功能提供另一个选项!

在本课中,我们将

  • 探索
  • 补充架构的 Query 类型,添加一个额外的端点
  • 传递到

介绍查询参数

现在是时候给我们的 Query 类型添加另一个入口点——以及一个新的 API 功能!

虽然我们可以从 REST API 获取特色房源列表,但我们还没有办法请求 特定 房源的详细信息。这个单一的 Query 入口点无法满足我们所有 mockups 的需求;而且我们的 Listing 类型仍然缺少一些 来保存其描述和便利设施!

A screenshot of a single listing in Airlock, focused on two additional required fields

查询 /listings/{listing_id} 端点

让我们回到我们的房源 REST API 端点。到目前为止,我们只探索了 GET /featured-listings 端点,但我们还有另一个选项可以用于获取特定房源的数据: GET /listings/{listing_id}

我们可以使用此链接 来请求特定房源。(请注意,我们已经将值 listing-1 代入 {listing_id} !)

GET /listings/{listing_id} 端点
https://rt-airlock-services-listing.herokuapp.com/listings/listing-1

以下是响应中包含的一些属性的代码片段

{
"id": "listing-1",
"title": "Cave campsite in snowy MoundiiX",
"description": "Enjoy this amazing cave campsite in snow MoundiiX, where you'll be one with the nature and wildlife in this wintery planet. All space survival amenities are available. We have complementary dehydrated wine upon your arrival. Check in between 34:00 and 72:00. The nearest village is 3AU away, so please plan accordingly. Recommended for extreme outdoor adventurers.",
// ... more listing properties
"amenities": [
{
"id": "am-2",
"category": "Accommodation Details",
"name": "Towel"
},
{
"id": "am-10",
"category": "Space Survival",
"name": "Oxygen"
}
// ... other amenities
]
}

这里有我们需要的一切(以及一些我们很快就会用到的额外属性)!

为了将此功能引入我们的 API,我们需要在架构中添加另一个入口点。我们可以指定 哪个 唯一的房源我们正在 ,通过给这个 一个 参数

🤔 如何使用参数

一个 是您为特定 提供的查询值。架构定义了每个字段接受的参数。

您的 可以使用 提供的 来帮助确定如何填充该字段的数据。参数可以帮助您检索特定对象、过滤一组对象,甚至转换字段的返回值。执行搜索的 通常将用户的搜索词作为参数提供。

为了在架构中为 定义一个 ,我们在字段名称后面添加括号。在括号内,我们编写参数名称,后面跟着一个冒号,然后是该参数的类型,例如 StringInt 。如果我们有多个 ,我们可以用逗号分隔它们。

添加 listing 字段

打开 schema.graphql 文件,并将以下 添加到 Query 类型。

schema.graphql
listing: Listing

我们将给这个 一个描述,然后在 listing 后面添加括号来指定它的 id ,类型为 ID!

schema.graphql
"Returns the details about this listing"
listing(id: ID!): Listing

就是这样!我们的 定义已经更新!现在我们的架构已经更新,可以用于我们正在实现的功能。继续前进到我们的 ListingAPI :让我们添加一个新的方法来访问这个端点!

更新 ListingAPI

让我们回到 ListingAPI 类,在 listing-api.ts 中,给它一个新的方法,可以访问这个端点。

在我们的 getFeaturedListings 方法下面,我们将添加一个名为 getListing 的新方法,它接受一个 listingId 参数,该参数是一个 string

datasources/listing-api.ts
getListing(listingId: string) {
// TODO
}

此方法将发出 GET 请求到我们刚刚测试的 /listings/{listing_id} 端点。我们可以使用 this.get 方法,将端点传递给它,并将 listingId 包括在内。然后,我们将返回结果!

listing-api.ts
getListing(listingId: string) {
return this.get(`listings/${listingId}`);
}

我们的方法的返回值类型仍然是 Promise<any> 。我们知道 Promise 将解析为一个单一的 Listing 类型,因此我们可以在这里更新它。我们还将 Listing 作为类型 传递给我们的 this.get 方法。

listing-api.ts
getListing(listingId: string): Promise<Listing> {
return this.get<Listing>(`listings/${listingId}`);
}

接下来是 函数。

在解析器中使用参数

打开 resolvers.ts 文件。按照与架构相同的结构,我们将添加一个新键,位于 Query 对象内部(就在 featuredListings 函数下面),名为 listing

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

当我们 我们 API 获取 listing 时,我们传递的 id 会自动传递到这个

要访问该 ,我们需要使用 的第二个位置参数, argsargs 是一个包含所有 的对象,这些参数是为 提供的。我们可以解构这个对象来访问 id 属性。我们还需要解构第三个位置参数,以获取其 dataSources 属性。

resolvers.ts
listing: (_, { id }, { dataSources }) => {},

接下来,在 函数体中,我们将使用 dataSources.listingAPI.getListing,并将 id 传递给它。

resolvers.ts
listing: (_, { id }, { dataSources }) => {
return dataSources.listingAPI.getListing(id);
},

我们需要更新我们生成的类型来考虑这个新的 listing 在我们的模式中,但是,趁我们现在在这里,让我们更新我们的项目,让它 自动 在模式发生变化时重新生成类型。

进入 package.json。我们要调整我们的 devgenerate 脚本。

package.json
"dev": "concurrently \"ts-node-dev --respawn --watch ./**/*.graphql ./src/index.ts\" \"npm run generate --watch\"",

此更新使我们能够同时运行两个脚本:第一个是 ts-node-dev,我们之前使用过它。此命令会在项目中进行更改时从 index.ts 重新启动我们的应用程序。我们运行的第二个脚本是 npm run generate,带有 --watch 标志。

接下来,让我们更新我们的 generate 脚本。

package.json
"generate": "graphql-codegen --watch \"src/schema.graphql\""

此添加使我们能够明确地告诉 代码生成器监视我们的 src/schema.graphql 文件,并重新运行代码生成过程。因此,无论何时我们的项目发生更改,我们都会获得与以前一样的热重载——以及每次模式发生更改时更新的 types.ts 文件!

好的,让我们试试。使用以下命令重新启动您的服务器

npm run dev

我们应该看到更多输出——首先也是最重要的是,类型已生成——其次是服务器正在运行的正常输出。

测试 listing 字段

让我们回到 https://127.0.0.1:4000 的资源管理器。

文档 面板中,我们将看到我们的 Query 类型包含我们新的 listing 。当我们点击它时,我们甚至可以看到它作为 接收的数据名称和类型。让我们添加一个新的工作区选项卡,然后单击 listing 旁边的加号按钮将其添加到我们的 中。

资源管理器会自动插入一些语法,使我们更容易完成

https://127.0.0.1:4000

A screenshot of the Explorer, with the listing field added to the Operation panel

让我们将我们的 更新为 GetListing 以更清楚地说明我们对请求数据的操作。

query GetListing($listingId: ID!) {
listing(id: $listingId) {
}
}

您会注意到这里有新东西:一个美元符号 ($) 后面跟着 listingId

$ 符号表示 中。该符号后的名称是我们的 的名称,我们可以在整个 中使用它。冒号后面的则是变量的类型,它必须与我们将使用的 的类型相匹配。变量非常有用——它们使我们能够从客户端动态传递参数值,因此我们无需将值硬编码到查询中。每次创建具有参数的查询时,我们都会使用它们。

在本例中,我们有一个名为 listingId,资源管理器为我们在下面的 变量 部分中设置了它。目前,它设置为 null,但是让我们用我们一直在测试的列表 ID 替换它: listing-1

将以下内容添加到资源管理器的 变量 部分中:

{ "listingId": "listing-1" }

让我们通过为我们想要的列表添加更多 来测试我们的 titlenumOfBeds

https://127.0.0.1:4000

A screenshot of the Explorer, building out the listing query to include its title and numOfBeds fields

资源管理器的 操作 面板现在应该如下所示:

query GetListing($listingId: ID!) {
listing(id: $listingId) {
title
numOfBeds
}
}

当我们单击运行 按钮时,我们看到了预期的数据!

这很好用,但是我们仍然缺少一些 来完成我们对单个列表的模拟。当我们检查列表的 REST API 响应时,我们会看到每个列表 JSON 对象都包含一个 "description" 键;因此,让我们继续更新我们的 Listing 类型以包含它。

回到 schema.graphql 中,添加下面显示的 description

schema.graphql
type Listing {
id: ID!
"The listing's title"
title: String!
"The listing's description"
description: String!
"The number of beds available"
numOfBeds: Int!
"The cost per night"
costPerNight: Float!
"Indicates whether listing is closed for bookings (on hiatus)"
closedForBookings: Boolean
}

但是,我们模拟的单个列表中显示的便利设施列表呢?

A screenshot of a single listing in Airlock, focused on two additional required fields

我们仍然缺少每个便利设施的这些数据——甚至在我们的 JSON 响应中,它看起来也比我们迄今为止探索的模式 更复杂一些——每个便利设施都有一个 "id""name""category"

一个列表的便利设施数据片段
{
"id": "listing-1",
// ... other listing properties
"amenities": [
{
"id": "am-2",
"category": "Accommodation Details",
"name": "Towel"
},
{
"id": "am-10",
"category": "Space Survival",
"name": "Oxygen"
}
// ... other amenities
]
}

让我们在下一课中解决将便利设施数据引入我们的 API 的问题。

练习

我们可以在模式中添加哪些入口点?
以下哪些是查询中使用参数的原因?
变量
变量用 
 
符号表示。它们用于为 
 
提供动态值,以避免在查询中包含 
 
 值。每个变量的类型都必须与 
 

中指定的类型匹配。将以下方框中的项目拖放到上面的空格中。

  • @

  • 解析器

  • $

  • !

  • 名称

  • 模式

  • 硬编码

  • 参数

关键要点

  • 允许我们过滤、自定义和进一步指定我们想要查询的数据。
  • 我们可以通过一个 通过它的 函数的第二个位置参数, args
  • 符号 $ 表示一个 中。符号 $ 后面的名称是我们的 的名称,我们可以在整个 中使用。冒号后是变量的类型,它必须与我们要使用的 的类型匹配。

接下来

在下一课中,我们将探索如何在 之间建立关系——具体来说,是在我们的 Listing 类型和我们将要创建的新类型 Amenity 之间建立关系。

上一页

分享你关于本课的疑问和评论

本课程目前处于

测试版
.你的反馈可以帮助我们改进!如果你卡住了或感到困惑,请告诉我们,我们会帮助你。所有评论都是公开的,必须遵守 Apollo行为准则。请注意,已经解决或处理的评论可能会被删除。

你需要一个GitHub账户才能在下面发布。没有账户吗? 请在我们的Odyssey论坛上发布。