PostGraphile 连接(Connections)指南:基于 Relay 规范的游标分页与增强实践
后端API网关【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址https://gitcode.com/gh_mirrors/cry/crystal点击查看免费下载导读本文围绕 PostGraphile v4 文档中的 Connections 章节系统讲解 GraphQL API 中面向大型数据库记录列表的 Connection 模式从 Relay Cursor Connections Specification 的合规实现到 PostGraphile 在其之上的三处增强totalCount、nodes、PageInfo.startCursor/endCursor再到基于condition参数的过滤能力以及用--simple-collections在连接与简单列表之间切换的实战方案。读完本文你将掌握 PostGraphile 连接分页的完整形态、底层行为控制方式以及如何结合仓库源码与测试用例验证连接行为的细节。一、为什么需要 Connection从裸列表到游标分页当一个 GraphQL 字段预期返回大量数据库记录时直接暴露一个普通的列表类型会带来几个问题客户端无法分页、服务端无法限制单次返回量、无法稳定地表达下一页的位置。PostGraphile 对此的答案是按照 Relay Cursor Connections Specification 实现一个 Connection并做少量增强以支持基于游标cursor的分页。这种做法的价值在于游标分页cursor-based pagination相比基于 offset 的分页更稳定——游标指向集合中的确定位置不受插入/删除数据的影响Connection 是 GraphQL 生态中被广泛认可的实践Relay 客户端可以开箱即用地消费服务端可以通过first/last/before/after参数控制返回窗口避免一次性取出全部数据。PostGraphile 中凡是来自表table、视图view以及关联关系relation的连接字段都遵循这一模式例如allPeople、allPosts这类根查询字段以及嵌套在节点下的关联字段如personByAuthorId、friends等。二、PostGraphile 在 Relay 规范之上的三处增强严格遵循 Relay 规范的 Connection 已经包含edges、pageInfo等结构但 PostGraphile 在其之上增加了三个实用增强这是使用 PostGraphile 连接时最常接触到的部分。2.1totalCount匹配查询的总记录数totalCount返回匹配查询条件的总记录数且明确不包含游标cursor、limit、offset 等分页约束的影响。也就是说即使你只取第一页的 10 条记录totalCount依然返回整个集合的大小这对显示共 N 条结果、计算总页数等场景非常关键。在仓库测试 connections-totalCount.test.graphql 中可以清晰看到它的用法——它不仅可以在连接顶层查询还可以嵌套在节点下使用query { a: allPeople { totalCount } b: allPeople { nodes { friends { totalCount } } } c: tableSetQuery { totalCount } }注意b中的嵌套用法totalCount统计的是当前上下文该用户的朋友列表的总数而非全表总数这正是匹配查询的总记录数的含义。2.2nodes跳过 edge 包装直达节点标准的 Relay Connection 要求通过edges { cursor, node { ... } }访问数据其中每个edge都带有一个游标。但当你不需要为每条记录单独取游标、只需要一个简单数据结构时edge包装就是多余的。PostGraphile 为此直接提供了nodes字段——只返回节点数组没有edge包装。在 connections.test.graphql 中可以看到这种精简用法o: allEdgeCases(condition: { rowId: 2 }) { nodes { rowId } }2.3PageInfo.startCursor与PageInfo.endCursor配合nodes使用由于nodes不携带每条记录的游标当你需要通过nodes { ... }分页时就需要从pageInfo中取当前页的首尾游标来构造下一页/上一页请求。PostGraphile 因此补充了PageInfo.startCursor与PageInfo.endCursor两个字段分别表示当前页第一条与最后一条记录对应的游标。完整的分页查询模式如下同样摘自 connections.test.graphql 的 fragment 定义fragment personConnection on PeopleConnection { pageInfo { startCursor endCursor hasNextPage hasPreviousPage } totalCount edges { cursor node { id name email } } }在该测试中a: allPeople、b: allPeople(first: 2)、c: allPeople(last: 2)、f: allPeople(orderBy: PRIMARY_KEY_ASC, before: ...)、g: allPeople(orderBy: PRIMARY_KEY_ASC, after: ...)等大量用例覆盖了first/last/before/after的组合验证了前后向分页与游标的正确性。三、使用condition参数对连接进行过滤文档明确说明许多连接特别是来自表、视图和关联关系的连接支持通过condition参数过滤返回结果。3.1 基础用法condition允许你按字段的精确值进行过滤例如按username或枚举类型字段query { allPeople(condition: { username: Alice }) { nodes { id name } } allPosts(condition: { category: ARTICLE }) { nodes { headline } } }在 connections.test.graphql 中可以看到更丰富的条件组合包括等值过滤、NULL 过滤以及与分页参数联用j: allPosts(condition: { authorId: 2 }) { ...postConnection } l: allPosts(last: 1, orderBy: HEADLINE_ASC, condition: { authorId: 1 }) { ...postConnection } s: allPeople(condition: { about: null }) { ...postConnection } u: allPeople(condition: { lastLoginFromIp: 192.168.0.1 }) { ...personConnection } w: allPeople(condition: { lastLoginFromSubnet: 192.168.0.0/24 }) { ...personConnection } x: allPeople(condition: { userMac: 0000.0000.0000 }) { ...personConnection }可见condition可以与first/last/orderBy任意组合这也是 PostGraphile 自带的基础过滤能力。3.2 过滤能力的性能考量PostGraphile 官方文档对过滤的实现给出明确的性能建议详见 过滤指南可以使用omit filter智能标签smart tag将某些字段从过滤条件列表中排除避免为不必要字段生成过滤入口可以使用--no-ignore-indexes选项自动省略那些看起来没有索引的字段的过滤条件防止生成低效 SQL。同时文档强调PostGraphile 官方强烈不建议引入通用的、功能强大的过滤插件例如支持大于/小于/范围/关联表过滤的通用方案因为这会带来难以预料的性能风险更推荐的做法是使用condition这类简单精确的过滤或通过自定义查询custom queries、计算列computed columns、makeExtendSchemaPlugin添加非常具体的过滤字段。四、--simple-collections在连接与简单列表之间切换4.1 三个取值如果你更喜欢简单的列表接口而非 Connection可以通过--simple-collections选项切换。它接受三个值取值行为omit默认值。只生成 Relay Connection不生成简单列表。both同时生成 Connection 与简单列表二者并存。only只生成简单列表XxxList形态不生成 Relay Connection。CLI 中的完整参数说明见 usage-cli.mdx--simple-collections omit|both|only omit (default) - relay connections only, only - simple collections only (no Relay connections), both - both4.2 源码级实现行为Behavior字符串如何起作用从源码结构看simpleCollections选项最终会被翻译成一组 Graphile Build 的行为字符串behavior string用于控制 schema 中哪些对象形态被启用。在 v4 preset 实现 中可以看到这一转换逻辑const simpleCollectionsBehavior ((): GraphileBuild.BehaviorString[] { switch (options.simpleCollections) { case both: { return [connection, resource:connection, list, resource:list]; } case only: { return [-connection, -resource:connection, list, resource:list]; } case omit: { return [connection, resource:connection, -list, -resource:list]; } default: { return []; } } })();这里的行为字符串如connection、list带有/-语义both同时启用connection与list两种形态only禁用-connection连接、启用list列表omit启用连接、禁用-list列表。这些行为字符串随后被注入到 schema 的globalBehavior中见同一文件中的makeV4Pluginschema: { globalBehavior(behavior) { return [ behavior, ...simpleCollectionsBehavior, -singularRelation:resource:connection, -singularRelation:resource:list, condition:attribute:filterBy, attribute:orderBy, resource:connection:backwards, ]; }, ... }从这段代码可以推断除了simpleCollections之外v4 兼容层还默认启用了condition:attribute:filterBy——为属性生成基于condition的过滤行为即本文第三部分讲解的过滤能力attribute:orderBy——为属性生成排序行为resource:connection:backwards——允许连接向后分页last/before。4.3 简单列表的查询形态当启用简单列表后查询字段会以XxxList的形式出现。仓库中的 simple-collections.test.graphql配置simpleCollections: both展示了其用法query { a: allPeopleList { ...personFragment } b: allPeopleList(first: 2) { ...personFragment } c: allPeopleList(orderBy: NAME_ASC) { ...personFragment } e: allPostsList(condition: { authorId: 2 }) { ...postFragment } g: allPeopleList(first: 3, offset: 1) { ...personFragment } k: allPostsList(orderBy: [AUTHOR_ID_DESC, HEADLINE_DESC], first: 3) { ...postFragment } }可以看到即使使用简单列表形态first限制条数、offset偏移、orderBy排序、condition过滤等参数依然可用只是返回结构从edges/cursor/pageInfo换成了直接的节点数组——更轻量、更符合简单数据结构的需求。此外schema 快照测试 simple-collections.test.ts 验证了在simpleCollections: both配置下会同时打印出简单列表与 Relay Connection 两种形态的 schema。4.4 程序化配置方式除了 CLI 的--simple-collections在以库的方式使用 PostGraphile 时该选项对应V4Options.simpleCollections类型定义在 v4.ts 中/** * - only: connections will be avoided, preferring lists * - omit: lists will be avoided, preferring connections * - both: both lists and connections will be generated */ simpleCollections?: only | both | omit;在.postgraphilerc.js配置文件中对应键为simpleCollections: [omit|both|only]见 usage-cli.mdx 的 RC 文件选项清单。三种配置方式CLI 标志、RC 文件、库 API最终都会汇入同一个makeV4Preset流程因此行为完全一致。五、连接相关的其他实践要点5.1 关联关系中的连接连接不仅存在于根查询字段也存在于节点之间的关联字段。例如在allPosts的node中访问personByAuthorId或在allPeople的node中访问friends时这些关联同样以连接/列表形态暴露。从源码结构看v4 兼容层默认禁用了单数关联singular relation的连接与列表形态见上文globalBehavior中的-singularRelation:resource:connection与-singularRelation:resource:list复数关联one-to-many则正常生成连接。5.2 与分页参数配合的完整模式综合仓库测试用例一个典型的、生产可用的分页查询是query PeoplePage($after: Cursor, $first: Int) { allPeople(orderBy: PRIMARY_KEY_ASC, after: $after, first: $first) { pageInfo { hasNextPage hasPreviousPage startCursor endCursor } totalCount nodes { id name email } } }客户端流程为首次请求不传after读取第一页从pageInfo.endCursor或最后一条edge.cursor取出游标作为下一次请求的after参数用pageInfo.hasNextPage判断是否还有下一页用totalCount展示结果总数。5.3 测试即文档验证连接行为仓库的查询测试如 connections.test.graphql、connections-totalCount.test.graphql、connections.boolean.test.graphql覆盖了连接在多种参数组合下的行为包括正向/反向分页、游标复用、condition组合、orderBy数组排序等。阅读这些.graphql测试文件配合同目录下的.json5期望输出是理解连接语义最直接的途径。六、总结PostGraphile 的连接实现以 Relay Cursor Connections Specification 为基准并做了三处实用增强totalCount——返回忽略分页约束的总记录数nodes——跳过 edge 包装直接获取节点数组PageInfo.startCursor/endCursor——为使用nodes分页提供首尾游标。在此基础上连接支持condition参数进行精确过滤并可通过--simple-collections omit|both|only或等价配置在 Relay Connection 与简单列表之间自由切换。无论是构建面向移动端/Web 的分页 API还是追求最简返回结构这套机制都能提供开箱即用的方案而仓库源码v4.ts与查询测试则为你验证和理解这些行为提供了第一手依据。赞分享后端API网关【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址https://gitcode.com/gh_mirrors/cry/crystal点击查看免费下载相关推荐Spectrum 的 GraphQL 分页实战基于 Relay Connections 规范的游标分页指南Spectrum 的 GraphQL 分页实战基于 Relay Connections 规范的游标分页指南 本文以 Spectrum 开源项目Simple,后端前端即时通讯社交PostGraphile Connections 完整指南Relay 游标分页增强、行为配置与性能基准PostGraphile Connections 完整指南Relay 游标分页增强、行为配置与性能基准 PostGraphile 为所有返回大量数据库记录的字后端API网关Relay Connections 指南基于游标的分页、连接更新与连接身份管理Relay Connections 指南基于游标的分页、连接更新与连接身份管理 导读 本文以 Relay v14 官方文档《Connections》为骨架完前端开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考