概述
我们现在可以查询 来自我们 REST API 的数据,但只能询问 featuredListings
。让我们给我们的 查询 功能提供另一个选项!
在本课中,我们将
- 探索 查询 参数 在 GraphQL
- 补充架构的
Query
类型,添加一个额外的端点 - 将 变量 传递到 GraphQL 查询
介绍查询参数
现在是时候给我们的 Query
类型添加另一个入口点——以及一个新的 API 功能!
虽然我们可以从 REST API 获取特色房源列表,但我们还没有办法请求 特定 房源的详细信息。这个单一的 Query
入口点无法满足我们所有 mockups 的需求;而且我们的 Listing
类型仍然缺少一些 字段 来保存其描述和便利设施!
查询 /listings/{listing_id}
端点
让我们回到我们的房源 REST API 端点。到目前为止,我们只探索了 GET /featured-listings
端点,但我们还有另一个选项可以用于获取特定房源的数据: GET /listings/{listing_id}
。
我们可以使用此链接 来请求特定房源。(请注意,我们已经将值 listing-1
代入 查询 变量 , {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]}
这里有我们需要的一切(以及一些我们很快就会用到的额外属性)!
为了将此功能引入我们的 GraphQL API,我们需要在架构中添加另一个入口点。我们可以指定 哪个 唯一的房源我们正在 查询 ,通过给这个 字段 一个 参数 。
🤔 如何使用参数
一个 参数 是您为特定 字段 提供的查询值。架构定义了每个字段接受的参数。
您的 解析器 可以使用 字段 提供的 参数 来帮助确定如何填充该字段的数据。参数可以帮助您检索特定对象、过滤一组对象,甚至转换字段的返回值。执行搜索的 查询 通常将用户的搜索词作为参数提供。
为了在架构中为 字段 定义一个 参数 ,我们在字段名称后面添加括号。在括号内,我们编写参数名称,后面跟着一个冒号,然后是该参数的类型,例如 String
或 Int
。如果我们有多个 参数 ,我们可以用逗号分隔它们。
添加 listing
字段
打开 schema.graphql
文件,并将以下 字段 添加到 Query
类型。
listing: Listing
我们将给这个 字段 一个描述,然后在 listing
后面添加括号来指定它的 参数 : id
,类型为 ID!
。
"Returns the details about this listing"listing(id: ID!): Listing
就是这样!我们的 查询 定义已经更新!现在我们的架构已经更新,可以用于我们正在实现的功能。继续前进到我们的 ListingAPI
:让我们添加一个新的方法来访问这个端点!
更新 ListingAPI
让我们回到 ListingAPI
类,在 listing-api.ts
中,给它一个新的方法,可以访问这个端点。
在我们的 getFeaturedListings
方法下面,我们将添加一个名为 getListing
的新方法,它接受一个 listingId
参数,该参数是一个 string
。
getListing(listingId: string) {// TODO}
此方法将发出 GET
请求到我们刚刚测试的 /listings/{listing_id}
端点。我们可以使用 this.get
方法,将端点传递给它,并将 listingId
参数 包括在内。然后,我们将返回结果!
getListing(listingId: string) {return this.get(`listings/${listingId}`);}
我们的方法的返回值类型仍然是 Promise<any>
。我们知道 Promise
将解析为一个单一的 Listing
类型,因此我们可以在这里更新它。我们还将 Listing
作为类型 变量 传递给我们的 this.get
方法。
getListing(listingId: string): Promise<Listing> {return this.get<Listing>(`listings/${listingId}`);}
接下来是 解析器 函数。
在解析器中使用参数
打开 resolvers.ts
文件。按照与架构相同的结构,我们将添加一个新键,位于 Query
对象内部(就在 featuredListings
函数下面),名为 listing
。
Query: {featuredListings: (_, __, { dataSources }) => {return dataSources.listingAPI.getFeaturedListings();},listing: () => {}},
当我们 查询 我们 GraphQL API 获取 listing
字段 时,我们传递的 id
参数 会自动传递到这个 解析器。
要访问该 参数,我们需要使用 解析器 的第二个位置参数, args
。 args
是一个包含所有 GraphQL 参数 的对象,这些参数是为 字段 提供的。我们可以解构这个对象来访问 id
属性。我们还需要解构第三个位置参数,以获取其 dataSources
属性。
listing: (_, { id }, { dataSources }) => {},
接下来,在 解析器 函数体中,我们将使用 dataSources.listingAPI.getListing
,并将 id
传递给它。
listing: (_, { id }, { dataSources }) => {return dataSources.listingAPI.getListing(id);},
我们需要更新我们生成的类型来考虑这个新的 listing
字段 在我们的模式中,但是,趁我们现在在这里,让我们更新我们的项目,让它 自动 在模式发生变化时重新生成类型。
进入 package.json
。我们要调整我们的 dev
和 generate
脚本。
"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
脚本。
"generate": "graphql-codegen --watch \"src/schema.graphql\""
此添加使我们能够明确地告诉 GraphQL 代码生成器监视我们的 src/schema.graphql
文件,并重新运行代码生成过程。因此,无论何时我们的项目发生更改,我们都会获得与以前一样的热重载——以及每次模式发生更改时更新的 types.ts
文件!
好的,让我们试试。使用以下命令重新启动您的服务器
npm run dev
我们应该看到更多输出——首先也是最重要的是,类型已生成——其次是服务器正在运行的正常输出。
测试 listing
字段
让我们回到 https://127.0.0.1:4000 的资源管理器。
在 文档 面板中,我们将看到我们的 Query
类型包含我们新的 listing
字段。当我们点击它时,我们甚至可以看到它作为 参数 接收的数据名称和类型。让我们添加一个新的工作区选项卡,然后单击 listing
字段 旁边的加号按钮将其添加到我们的 查询 中。
资源管理器会自动插入一些语法,使我们更容易完成 查询。
让我们将我们的 操作名称 更新为 GetListing
以更清楚地说明我们对请求数据的操作。
query GetListing($listingId: ID!) {listing(id: $listingId) {}}
您会注意到这里有新东西:一个美元符号 ($
) 后面跟着 listingId
。
该 $
符号表示 变量 在 GraphQL 中。该符号后的名称是我们的 变量 的名称,我们可以在整个 查询 中使用它。冒号后面的则是变量的类型,它必须与我们将使用的 参数 的类型相匹配。变量非常有用——它们使我们能够从客户端动态传递参数值,因此我们无需将值硬编码到查询中。每次创建具有参数的查询时,我们都会使用它们。
在本例中,我们有一个名为 listingId
的 变量,资源管理器为我们在下面的 变量 部分中设置了它。目前,它设置为 null
,但是让我们用我们一直在测试的列表 ID 替换它: listing-1
。
将以下内容添加到资源管理器的 变量 部分中:
{ "listingId": "listing-1" }
让我们通过为我们想要的列表添加更多 字段 来测试我们的 查询: title
和 numOfBeds
。
资源管理器的 操作 面板现在应该如下所示:
query GetListing($listingId: ID!) {listing(id: $listingId) {titlenumOfBeds}}
当我们单击运行 查询 按钮时,我们看到了预期的数据!
这很好用,但是我们仍然缺少一些 字段 来完成我们对单个列表的模拟。当我们检查列表的 REST API 响应时,我们会看到每个列表 JSON 对象都包含一个 "description"
键;因此,让我们继续更新我们的 Listing
类型以包含它。
回到 schema.graphql
中,添加下面显示的 description
字段。
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}
但是,我们模拟的单个列表中显示的便利设施列表呢?
我们仍然缺少每个便利设施的这些数据——甚至在我们的 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
。 - 符号
$
表示一个 变量 在 GraphQL 中。符号$
后面的名称是我们的 变量 的名称,我们可以在整个 查询 中使用。冒号后是变量的类型,它必须与我们要使用的 参数 的类型匹配。
接下来
在下一课中,我们将探索如何在 对象类型 之间建立关系——具体来说,是在我们的 Listing
GraphQL 类型和我们将要创建的新类型 Amenity
之间建立关系。
分享你关于本课的疑问和评论
本课程目前处于
你需要一个GitHub账户才能在下面发布。没有账户吗? 请在我们的Odyssey论坛上发布。