模式拼接:丰富无头GraphQL架构中的数据
在这个视频中,Roy Derks向我们介绍了通过以下方式使用模式缝合的例子@graphql-tools/stitch的例子,并提供了一个带有演示代码的GitHub repo。
模式拼接是将不同服务的GraphQL模式组合成单一的、统一的GraphQL模式的艺术。其目的是产生一个网关服务,我们可以从中访问我们公司的所有服务。
模式缝合最早是由Apollo引入的,但他们决定停止使用,转而使用Apollo Federation。最近,The Guild接手了这个概念,重新实施并改进了以前的设计,产生了一个解决方案,提供了与联盟相当的好处,同时提供了一个更简单的思考问题的方法。
罗伊使用模式缝合来演示如何结合两个外部服务的数据。
- 一个内容管理系统,可(作为一个模拟)在下面使用
localhost:3001 - 一个电子商务的API,可在下面找到(作为模拟)。
localhost:3002
这两个数据源也与本地GraphQL服务器的模式相结合,可在localhost:3000 。
访问统一的GraphQL模式--来自Roy Derks演讲的屏幕截图
采用的堆栈是基于。
@graphql-tools/stitchjson-graphql-server用于快速模拟外部服务@graphql-tools/schema创建本地GraphQL模式- 用于本地GraphQL服务器的Apollo服务器
- Next.js用于通过API路由暴露API。
在Next.js下创建了一个API路由,它暴露了GraphQL终端。 api/graphql.js下创建了一个来自Next.js的API路由,它在localhost:3000/api/graphql 下暴露了GraphQL端点。我们可以通过GraphQL Playground与之互动,在浏览器中打开端点的URL。
GraphQL Playground的图片
API路由中合并模式的相关代码,以其最简单的形式,如下所示。
let localSchema = makeExecutableSchema({
// Code here to create the local GraphQL schema
});
export default async function grapqhl(req, res) {
// Setup subschema configurations
const localSubschema = { schema: localSchema };
const cmsSubschema = await createRemoteSchema({
url: 'http://localhost:3001/graphql/'
});
const productsSubschema = await createRemoteSchema({
url: 'http://localhost:3002/graphql/',
});
// Build the combined schema and set up the extended schema and resolver
const schema = stitchSchemas({
subschemas: [localSubschema, productsSubschema, cmsSubschema]
});
}
这段代码使用makeExecutableSchema 来设置本地GraphQL模式,使用createRemoteSchema 从localhost:3001/graphql 和localhost:3002/graphql 来加载远程GraphQL模式,最后使用stitchSchemas 将它们合并在一起。
接下来,我们需要将这些模式相互联系起来,这样我们就可以通过帖子的用户ID(由外部CMS提供)来检索产品(由电子商务API提供)。
{
cms_allPosts { # This data comes from the CMS
id
User {
name
Products { # This data comes from the e-commerce API
id
title
}
}
}
}
组合模式是在stitchSchemas 中配置的。
// Build the combined schema and set up the extended schema and resolver
const schema = stitchSchemas({
subschemas: [localSubschema, productsSubschema, cmsSubschema],
typeDefs: `
extend type Product {
cmsMetaData: [Cms_Product]!
}
`,
resolvers: {
Product: {
cmsMetaData: {
selectionSet: `{ id }`,
resolve(product, args, context, info) {
// Get the data for the extended type from the subschema for the CMS
return delegateToSchema({
schema: cmsSubschema,
operation: 'query',
fieldName: 'cms_allProducts',
args: { filter: { id: product.id } },
context,
info,
});
},
},
},
},
});
最后,我们必须检查不同的模式是否在相同的类型或字段名下暴露其数据。例如,电子商务API也可能有一个叫做Post 的类型,和/或在字段allPosts 下公开,从而产生冲突。
避免这些冲突是通过createRemoteSchema'stransforms 参数完成的,它允许我们将一个模式中的类型和字段重命名为另一个模式。在这种情况下,我们让CMS的子模式将其类型Post 改为Cms_Post ,字段allPosts 改为cms_allPosts 。
const cmsSubschema = await createRemoteSchema({
url: 'http://localhost:3001/graphql/',
transforms: [
new RenameRootFields(
(operationName, fieldName, fieldConfig) => `cms_${fieldName}`,
),
new RenameTypes((name) => `Cms_${name}`),
],
});
我们最终可以执行一个查询,从所有独立的服务中获取数据,通过一个单一的、统一的模式进行访问。
从合并的模式中执行GraphQL查询 - Roy Derks演讲中的截图
如前所述,如果你想查看带有代码的 repo或观看完整的 GraphQLConf 视频,这里有一些链接。
带有数据联合的GraphQL与GraphQL服务的联合
Tanmai Gopal是Hasura的联合创始人和首席执行官,该服务通过Postgres提供实时的GraphQL APIs。
Hasura目前正在建立他们自己的联盟版本,它是基于与Apollo Federation不同的架构。在他的演讲中,Tanmai向我们介绍了Hasura的联盟解决方案试图解决的所有挑战,以及为什么Apollo联盟的方法不能解决这些问题。
Tanmai首先描述了什么是联盟,以及何时和为何使用联盟是合理的。
然后,Tanmai深入探讨了关于联盟的两种不同思考方式。
- GraphQL服务的联盟(Apollo所遵循的方法),它让每个底层来源执行其查询的部分
- 联合数据的GraphQL,它在解决查询之前集中了来自不同服务的数据。
然后,Tanmai解释说,虽然这两种不同的方法产生了相同的结果,但当使用GraphQL on federated data的方法时,在性能上有相当大的提升。
Tanmai提供了几个查询的例子来描述为什么联合GraphQL服务不能很好地扩展。他强调,跨不同服务的数据聚合不能有效地完成,因为在解决包含跨服务的跨数据库连接的复杂查询时,网关对涉及的子域的所有实体没有一个统一的上下文。
最后,Tanmai解释说,Hasura正在研究一个基于使用GraphQL与联合数据的解决方案,它能够从不同的服务中获取所有相关数据,并从一个集中的位置解决查询,而不是让每个服务解决其查询的一部分,从而为复杂的查询提供更好的性能。
为了比较Apollo和Hasura的联盟方法,我们需要再等一等。Tanmai承诺这个新功能将很快推出,但没有提到具体时间。Hasura的网站也没有谈及这个问题,而且还没有任何文档。目前,我们只有这次会议的谈话。
然而,我想象Hasura的方法比Apollo的更有限制性,因为它很可能依赖于Hasura的基础设施来运作,其吸引力主要取决于Hasura的服务价格。另一方面,Apollo联盟被设计为一种基于指令分割图的方法,所以它不需要任何外部工具或基础设施(尽管Apollo也提供管理的联盟),使用GraphQL中预先存在的语法即可工作。
最后,Hasura的联盟方法应该与GraphQL工具的模式缝合进行对比,正如我们在之前的演讲中看到的,它可以以更简单的方式提供相同的结果。
迁移GitHub的全局ID
Andrew Hoglund是GitHub的API团队的一名高级软件工程师。在他的演讲中,Andrew分享了他的团队目前如何将GitHub的GraphQL API中所有实体的全局标识符迁移到不同的格式,他们为什么要这样做,以及他们所遇到的挑战。
我们可以通过查询任何对象上的字段id ,来可视化全局ID的格式。例如,获取 leoloso/PoP存储库对象的ID,可以通过这个GraphQL查询完成。
{
user(login: "leoloso") {
repository(name: "PoP") {
id
}
}
}
执行查询,我们得到了这个响应。
{
"data": {
"user": {
"repository": {
"id": "MDEwOlJlcG9zaXRvcnk2NjcyMTIyNw=="
}
}
}
}
仓库对象的ID是MDEwOlJlcG9zaXRvcnk2NjcyMTIyNw== 。这个格式有以下属性。
- 它是base64编码的
- 包含对象类型和对象ID
- 它的目的是不透明的
解码ID,例如通过Bash命令,将显示出存储的信息。
$ echo `echo MDEwOlJlcG9zaXRvcnk2NjcyMTIyNw== | base64 --decode`
010:Repository66721227
底层数据,010:Repository66721227 ,是由以下部分组成。
- 一个校验和
- 对象类型
- 数据库ID
这种简单的格式最初为GitHub提供了很好的服务,因为GitHub将其数据存储在一个单一的数据库中。实体的全局ID已经提供了在数据库中找到该实体并检索其数据所需的所有信息。
一段时间后,GitHub使用Vitess迁移到分片数据库,这是数据库内数据的水平分区。数据库分片可以提高性能,因为通过将所有数据分散在多个数据库中,每个数据库表的行数会减少--从而减少索引大小,使搜索速度加快--而且不同的分片可以放在不同的机器上,这样就可以为不同的数据片优化硬件。
当查询分片数据库时,全局ID格式变得不适合,尽管它仍然可以用来检索数据,因为实体的数据只存在于一个数据库中,而不是所有分片中,全局ID不会指出数据位于哪个数据库中。
为了定位数据,GitHub的GraphQL API必须对所有的数据库进行查询,而其中只有一个数据库会产生匹配,这使得查询的执行效率非常低。
因此,GitHub的API团队决定将全局ID迁移到一种新的格式,同时提供包含实体数据的数据库名称,这样就可以再次有效地进行检索。
这种新格式比以前的格式更复杂。它由两个元素组成。
- 一个类型提示,表明它是什么类型的实体
- 一个所有权方案,它包含检索该类型实体所需的数据
所有权方案是按实体定制的,因为不同的实体需要不同的数据来识别。例如,一个工作流的运行需要触发它的拉动请求的ID。
这种新的格式运行良好,使GitHub能够解决当前的问题,并预见到未来的一些问题。特别是,GitHub最终可能会以多区域的设置来存储数据,而新的格式也可以表明存储数据的区域名称。
由于这种新的ID格式与之前的格式不兼容,GitHub需要实施一个缓慢的、渐进的推广,以确保它不会破坏服务。为了完成这项任务,GitHub设置了一个废弃期,在此期间两种ID格式将共存,并创建了一些工具来帮助服务从旧格式迁移到新格式。
你可以观看视频以了解更多。
用GraphQL推动电子商务的发展
Stuart Guest-Smith是BigCommerce的主要架构负责人。在他的演讲中,Stuart探讨了商家如何利用GraphQL构建高性能、可扩展和个性化的电商体验。
Stuart在演讲开始时宣称。
电子商务要求企业做到快速、灵活和个性化。GraphQL在很大程度上使之成为可能。
一个电子商务服务通常会跨越多个软件,其中包括。
- 企业资源规划(ERP)
- 订单管理系统(OMS)
- 产品信息管理(PIM)
- 客户关系管理(CRM)
- 内容管理系统(CMS)
GraphQL使我们能够访问所有这些后端系统的数据并将其联系起来,实现电子商务的最新趋势之一,即 "可组合商务"。
来自这些多个后端系统的数据可以通过GraphQL API访问不同的前端,例如。
- 一个电商店面
- 一个原生移动应用程序
- 一个无头的CMS
- 一个无头的数字体验平台(DXP)
- 一个定制的渐进式网络应用程序(PWA)店面
这种灵活性使我们能够为用户创造一种个性化的、增强的体验。
然后,Stuart解释说,GraphQL可以通过使用BfF架构模式作为多个电子商务后端和多个前端之间的接口,其目的是为每个用户提供定制的后端体验。
GraphQL可以帮助提高性能,因为它可以只检索所需的数据,而不进行欠取或过取。但除此之外,我们必须添加一个缓存层,并管理查询的复杂性(或冒着让恶意行为者执行昂贵的查询,从而拖慢系统的风险)。
最后,Stuart解释说,GraphQL为电子商务带来的最重要的好处之一是客户体验的个性化。
总结
GraphQLConf 2021为我们带来了GraphQL生态系统目前正在讨论的内容,表明在GraphQL推出五年后,仍有大量的发展在发生,新的方法论正在创建,以解决社区的持续需求。
这篇文章总结了关于四个不同的新发展的谈话。
- Roy Derks解释了如何使用新的
@graphql-tools/stitch库进行模式拼接 - Tanmai Gopal展示了Hasura是如何创建Apollo Federation的竞争者服务的。
- Andrew Hoglund分享了GitHub GraphQL API的全局ID格式如何被迁移到一个新的格式中,该格式可以支持数据库分片和多区域设置。
- Stuart Guest-Smith解释了如何利用GraphQL,使用BfF模式,实现可组合的商务和个性化的用户体验。
尽管GraphQL已经很成熟了,但看到新的开发正在发生,还是非常令人激动的要知道还有什么正在发生,请查看GraphQLConf 2021的所有视频。