GraphQL-Ruby 的 GraphQL::Relay::RangeAdd:在 Mutation 中实现 Relay RANGE_ADD 连接更新 后端API设计【免费下载链接】graphql-rubyRuby implementation of GraphQL项目地址https://gitcode.com/gh_mirrors/gr/graphql-ruby点击查看免费下载导读本指南讲解 GraphQL-Ruby 为 Relay 的RANGE_ADD连接更新操作提供的官方辅助器GraphQL::Relay::RangeAdd如何在一个新增评论/新增商品类 Mutation 的返回值中同时交付更新后的完整连接connection与指向新条目的单条边edge让 Relay 客户端可以无缝完成列表增量更新。读完本文你将掌握RangeAdd的全部构造参数、与RelayClassicMutation的搭配方式、底层连接查找与补齐机制以及可覆写的range_add_edge钩子。背景Relay 的 RANGE_ADD 与更新连接难题在 Relay含 Relay Classic 及后续版本的规范中RANGE_ADD是专门用于向一个连接里新增条目的操作语义。它要求 Mutation 的响应不仅能返回新增对象本身还要能够返回更新后的连接包含新条目在内的完整列表用于刷新客户端缓存新条目的 edge包含新条目的节点与游标供客户端精准插入。GraphQL-Ruby 为此内置了GraphQL::Relay::RangeAdd辅助器该模块在 lib/graphql/relay.rb 中被直接require graphql/relay/range_add加载。正如官方文档 guides/relay/range_add.md 所描述它的职责非常聚焦给定一个条目集合和一个新条目产出对应的 connection 与 edge。RangeAdd 的核心 APIRangeAdd的定义位于 lib/graphql/relay/range_add.rb类注释第一句就点明其设计意图This provides some isolation fromGraphQL::Relayinternals.对GraphQL::Relay内部实现提供隔离。它的对外接口十分精简class GraphQL::Relay::RangeAdd attr_reader :edge, :connection, :parent def initialize(collection:, item:, context:, parent: nil, edge_class: nil) # ... end end构造参数一览参数是否必填默认值作用collection:是—待包装成连接的条目集合例如一个Array或ActiveRecord::Relationitem:是—刚刚新增的条目会被包装成edge_class或连接的默认 edge 类context:是—当前查询上下文GraphQL::Query::Context会传给 connectionparent:否nilcollection的所属对象如post会传给 connectionedge_class:否nil包装item时使用的 edge 类缺省时使用 connection 自身的 edge 类实例化后即可通过attr_reader暴露的三个读取器拿到结果range_add.connection、range_add.edge、range_add.parent。完整实战给评论列表添加一条新评论RangeAdd的类注释中自带一份可直接落地的示例见 lib/graphql/relay/range_add.rb 的example段post Post.find(args[:post_id]) comments post.comments new_comment comments.build(body: args[:body]) new_comment.save! range_add GraphQL::Relay::RangeAdd.new( parent: post, # collection 的所属对象 collection: comments, # 更新后的完整集合已包含新条目 item: new_comment, # 刚刚新增的条目 context: context, # 当前查询上下文 ) response { post: post, comments_connection: range_add.connection, new_comment_edge: range_add.edge, }关键点先完成新增动作再把包含新条目的完整集合作为collection传入。随后将range_add.connection与range_add.edge放入 Mutation 的返回 Hash分别对应 Schema 中的connection类型字段与edge类型字段。集成测试中的真实用法与 RelayClassicMutation 搭配仓库的集成测试 spec/integration/rails/graphql/relay/range_add_spec.rb 展示了RangeAdd在GraphQL::Schema::RelayClassicMutation中的完整落地。RelayClassicMutation见 lib/graphql/schema/relay_classic_mutation.rb自带input单参数约定与clientMutationId回写等 Relay Classic 惯例与RangeAdd是官方文档推荐的组合。add_item Class.new(GraphQL::Schema::RelayClassicMutation) do graphql_name AddItem argument :name, String argument :price, Integer argument :menu_idx, Integer field :item_edge, item.edge_type, null: false, hash_key: :item_edge field :items, item.connection_type, null: false, hash_key: :items field :menu, menu, null: false, hash_key: :menu define_method :resolve do |input| this_menu menus[input[:menu_idx]] new_item OpenStruct.new(name: input[:name], price: input[:price]) this_menu.items new_item range_add GraphQL::Relay::RangeAdd.new( parent: this_menu, item: new_item, collection: this_menu.items, context: context, ) { menu: range_add.parent, items: range_add.connection, item_edge: range_add.edge, } end end对应的客户端 Mutationmutation { addItem(input: {name: Chilaquiles, price: 699, menuIdx: 0}) { menu { name } itemEdge { node { name price } } items { edges { node { name } cursor } } } }测试断言验证了三条结果spec/integration/rails/graphql/relay/range_add_spec.rbitemEdge.node恰好是新增的Chilaquilesitems.edges返回完整列表[California Burrito, Fish Taco, Chilaquiles]新条目已追加在末尾各游标依次为__1、__2、__3——因为该测试为 Schema 配置了自定义游标编码器PassThroughEncoderencode时前缀__说明edge 的游标基于集合中的位置实时计算而不是复用某个缓存值。底层原理连接如何被创建与补齐RangeAdd只有约 20 行实现lib/graphql/relay/range_add.rb却连接了 GraphQL-Ruby 分页体系中的三个关键机制。1. 连接类查找context.schema.connections.wrapper_forconn_class context.schema.connections.wrapper_for(collection) connection conn_class.new(collection, parent: parent, context: context, edge_class: edge_class)wrapper_for是 lib/graphql/pagination/connections.rb 中Connections管理器提供的能力它沿items.class.ancestors逐级向上查找已注册的包装类wrappers[cls]找到即返回。该管理器在add_default中默认注册了若干常见集合类型的包装类lib/graphql/pagination/connections.rbArray→Pagination::ArrayConnectionActiveRecord::Relation→Pagination::ActiveRecordRelationConnectionSequel::Dataset→Pagination::SequelDatasetConnectionMongoid 各版本的Criteria/Enumerable→Pagination::MongoidRelationConnection因此RangeAdd无需关心collection具体是数组还是 ORM 关系它总能通过 Schema 的注册表拿到正确的连接实现如需为自定义集合注册包装类也可通过schema.connections.add(MyClass, MyConnection)扩展。2. 连接补齐ConnectionExtension#populate_connectionRangeAdd手工构造的连接不会收到来自客户端的first/after/last/before分页参数因此源码注释特意写明The rest will be added by ConnectionExtension其余部分将由 ConnectionExtension 补齐。当 Mutation 返回这个连接后运行时会经过 lib/graphql/schema/field/connection_extension.rb 的ConnectionExtensionresolve阶段first/last/before/after四个分页参数会被从用户方法入参中剔除并保留在memo中after_resolve阶段调用populate_connection若返回值已是GraphQL::Pagination::Connection实例则把context、parent、分页参数first_value/after_value/last_value/before_value、arguments、field、max_page_size、default_page_size以及字段自定义的edge_class依次补齐lib/graphql/schema/field/connection_extension.rb。也就是说RangeAdd只负责按已排好序的集合建连接分页参数与 Schema 级配置由字段扩展在返回阶段补全两者各司其职。3. edge 的生成range_add_edge钩子edge if connection.respond_to?(:range_add_edge) connection.range_add_edge(item) else connection.edge_class.new(item, connection) endPagination::Connection基类定义了range_add_edge方法lib/graphql/pagination/connection.rb其注释说明该方法由Relay::RangeAdd调用可被覆写以便根据 connection 的当前状态对item做调整def range_add_edge(item) edge_class.new(item, self) endrespond_to?检查则是为了兼容旧版 GraphQL-Pro 中未实现该方法的连接实现源码注释to support old versions of GraphQL-Pro。若连接类未提供range_add_edge则回退为connection.edge_class.new(item, connection)。进阶自定义 edge 与连接状态通过edge_class:指定 edge 包装类initialize的edge_class参数会被原样传给conn_class.new。若传入nil连接会采用自身的edge_class即self.class::Edge见 lib/graphql/pagination/connection.rb若传入自定义类则新建连接与生成 edge 时都会使用它。覆写range_add_edge实现带状态的新边当新条目需要根据连接当前状态如游标、分页切片、附加元数据生成特殊 edge 时可在自定义连接子类中覆写range_add_edge(item)返回任何item的包装对象。这为新边需要携带额外字段的场景提供了扩展点而无需改动RangeAdd本身。注意事项与适用前提集合须预先排序与分页RangeAdd的类注释明确指出The connection doesnt receive outside arguments, so the list of items should be ordered and paginated before providing it here.——传入的collection应当已经是业务上正确的顺序且如需分页应在传入前完成RangeAdd不会替你处理。先写入、再取集合务必先执行save!/等新增操作再把已包含新条目的完整集合传入否则返回的连接会缺少新条目。parent贯穿连接与 edgeparent会存入连接而 edge 的parent亦来自连接lib/graphql/pagination/connection.rb因此通过range_add.parent可同时取回所属对象。游标与 Schema 编码器绑定edge 的cursor在访问时由connection.cursor_for实时计算lib/graphql/pagination/connection.rb并使用 Schema 配置的cursor_encoder编码测试中通过self.cursor_encoder(PassThroughEncoder)验证了这一点。字段类型需与返回对象匹配连接类型字段应声明为item.connection_type、edge 字段声明为item.edge_type二者由 lib/graphql/schema/member/relay_shortcuts.rb 按TypeNameConnection/TypeNameEdge命名自动生成返回值分别对应range_add.connection与range_add.edge。验证配套集成测试仓库以集成测试固化了RangeAdd的契约spec/integration/rails/graphql/relay/range_add_spec.rb构造含两个菜品的Menu与Item类型通过AddItemMutation 新增第三个菜品随后断言返回的menu、itemEdge与更新后的items连接内容——这组测试既是行为说明也可作为自行实现RANGE_ADD时的对照样例。赞分享后端API设计【免费下载链接】graphql-rubyRuby implementation of GraphQL项目地址https://gitcode.com/gh_mirrors/gr/graphql-ruby点击查看免费下载相关推荐Relay 连接Connection更新实战在 Mutation/Subscription 中增删边与连接身份管理Relay 连接Connection更新实战在 Mutation/Subscription 中增删边与连接身份管理 本指南聚焦 Relay 中的一个高频实前端开发工具Relay useMutation Hook 深度实战在 React 组件中执行 GraphQL MutationRelay useMutation Hook 深度实战在 React 组件中执行 GraphQL Mutation useMutation 是 Relayr前端开发工具Relay 的 UploadableMap 类型在 Mutation 中实现 GraphQL 文件上传的权威指南Relay 的 UploadableMap 类型在 Mutation 中实现 GraphQL 文件上传的权威指南 UploadableMap 是 Relay前端开发工具上一篇Ice 菜单栏管理5 分钟完成 macOS 图标收纳、排序与外观定制的完整指南下一篇如何让AI数字人成为你的24小时智能管家Fay框架深度解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考