概述
是时候向我们的应用程序引入数据加载器了。
在此课程中,我们将
- 使用具有批处理功能的
DataLoader
类 - 更新我们的
getAmenities
方法,以便将每个房源 ID 传递给数据加载器 - 测试我们的新数据加载功能
添加数据加载器
让我们将 dataloader
包导入我们的应用程序。在项目的根目录中打开一个新的终端,并运行以下命令。
npm install --save dataloader
接下来,我们将跳转到我们的 ListingAPI
类。在此,我们将引入一个执行以下操作的新方法:
- 创建一个新的
DataLoader
- 提供一个批处理函数,该函数收集
listingIds
并向 REST API 发出单个请求
在文件顶部,从 dataloader
包中导入 DataLoader
。
import DataLoader from "dataloader";
在我们的 ListingAPI
类中,让我们为我们的数据加载器实例化设置一个新的 private
字段,仅可从中获取该字段 仅在此类中的数据访问。
private batchAmenities = new DataLoader()
DataLoader
构造函数接受批量加载函数,我们将在内联中定义该函数。此批量函数接受一个键数组,并返回一个将解析为一个值数组的 Promise。对于键数组,我们将设置一个名为 listingIds
的参数,并且还可以添加我们的返回类型注释: Promise<Amenity[][]>
。
private batchAmenities = new DataLoader((listingIds): Promise<Amenity[][]> => {// TODO})
接下来,我们将使此函数变成 async
,并 await
调用 REST 端点 GET /amenities/listings
的结果。
private batchAmenities = new DataLoader(async (listingIds): Promise<Amenity[][]> => {await this.get<Amenity[][]>("amenities/listings");});
此端点需要一个查询参数,该参数称为ids
,它是逗号分隔的列表 ID 字符串。我们将此作为第二个参数添加到this.get
函数,一个带有params
属性的对象,传递另一个包含查询参数ids
的对象。由于我们的数据加载器将其listingIds
接收为一个数组,我们将使用join
方法,指定逗号作为每个值的间隔符。
private batchAmenities = new DataLoader(async (listingIds): Promise<Amenity[][]> => {await this.get<Amenity[][]>("amenities/listings", {params: {ids: listingIds.join(","),},});});
最后,让我们在名为amenitiesLists
的新常量中捕获调用端点得到的结果,然后返回结果。
private batchAmenities = new DataLoader(async (listingIds): Promise<Amenity[][]> => {const amenitiesLists = await this.get<Amenity[][]>("amenities/listings", {params: {ids: listingIds.join(","),},});return amenitiesLists;});
在继续之前,让我们添加几个日志陈述,以便我们可以关注该方法的工作方式。我们将记录传入数据加载器的listingIds
列表和amenitiesLists
结果。
private batchAmenities = new DataLoader(async (listingIds): Promise<Amenity[][]> => {console.log("Making one batched call with ", listingIds);const amenitiesLists = await this.get<Amenity[][]>("amenities/listings", {params: {ids: listingIds.join(","),},});console.log(amenitiesLists);return amenitiesLists;});
我们的数据加载器方法已经处理好了; 现在我们需要从该类内部调用它。
更新 getAmenities
向下滚动类,我们将找到解析器一直以来调用的getAmenities
方法,即我们的Listing.amenities
与其为每个列表调用GET /listings/:id/amenities
端点,我们将更新自己的方法,以将接收的每个列表 ID 传递给数据加载器。我们可以通过调用私有字段上的load
方法batchAmenities
来实现此目的。
getAmenities(listingId: string): Promise<Amenity[]> {console.log("Making a follow-up call for amenities with ", listingId);- return this.get<Amenity[]>(`listings/${listingId}/amenities`);+ return this.batchAmenities.load(listingId);}
我们在此更新日志陈述:与其报告我们正在提出进行后续请求,不如更改措辞,使其清楚我们正在将一个特定的列表 ID 传递给数据加载器。
getAmenities(listingId: string): Promise<Amenity[]> {- console.log("Making a follow-up call for amenities with ", listingId);+ console.log("Passing listing ID to the data loader: ", listingId);return this.batchAmenities.load(listingId);}
就是我们的 数据源 类!并且我们无需对我们的 解析器进行任何更改:它将继续调用 getAmenities
方法,该方法将委派责任,将所有请求的列表 ID 中的数据收集并加载到单个 查询中。让我们进行测试!
测试数据加载器
确保您的服务器仍在运行,让我们返回沙盒环境。
我们再次对特色列表及其便利设施运行相同的 查询。
query GetFeaturedListingsAmenities {featuredListings {idtitleamenities {idnamecategory}}}
当我们执行 查询时,我们获取到的数据与之前完全一样!但让我们检查一下底层发生了什么,回到我们的终端。
首先,我们将看到三行:针对特色列表请求中涉及的每个列表 ID 各一行。
Passing listing ID to the data loader: listing-1Passing listing ID to the data loader: listing-2Passing listing ID to the data loader: listing-3
这些行后面紧接着输出内容来自我们的 单一 批处理调用:
Making one batched call with [ 'listing-1', 'listing-2', 'listing-3' ]
然后我们应看到打印出一个大数组:它包含多个较小的数组,每个数组又包含 多个便利设施!这些是我们请求的每个列表 ID 的便利设施列表。我们的列表 ID 已成功批处理为一个请求! 👏👏👏
练习
要点
- 我们为每个请求创建一个新的
DataLoader
,向其构造函数传递一个批处理函数,该函数接收一个键数组,并返回一个值数组。 - 若要实际使用数据加载器,我们调用其
load
方法并传入每个键。 - GraphQL
dataloader
包具有缓存优点,并且会对作为请求的键进行重复数据删除。
恭喜!
完成这些操作后,大功告成了!您已解决 n+1 问题,有了工具包中的数据加载器,您具有提升应用程序效率的资源。我们在应用程序中采用了一种基本的数据获取策略,并将其转化为能够随着查询和功能扩展的功能。通过使用数据加载器,我们见识了如何在网络中发出更少、更有效率的请求,以比以往任何时候都更快的速度提供数据。
下一步:了解如何使用 GraphQL API 以及新域增长您的内容 使用 Apollo Server 及 TypeScript 进行联合。我们将介绍构建联合 图形 的最佳实践,以及 GraphOS 工具,这些工具将帮助您轻松、可观察且高性能地实现健壮的企业 API!
感谢您参加此课程,我们期待在下一课程中与您相会。
在这节课中分享你的问题和评论
本课程目前正在
您需要一个 GitHub 帐户才能在下面进行发帖。没有? 转而发帖至 Odyssey 论坛。