如何在GraphQL游标分页场景下避免代码重复
错误根因
报错的核心原因是你封装的PageType、Edge工厂函数生成的GraphQL类型名是固定值,多次调用为不同实体生成分页类型时,会出现重复的类型名,不符合GraphQL schema的唯一性要求。
解决方案
第一步:修改分页工具类,动态生成类型名
修改pagination.js中的Edge和PageType函数,用传入的实体类型名拼接生成唯一的分页类型名:
const Edge = (itemType) => { return new GraphQLObjectType({ // 动态生成唯一的Edge类型名 name: `${itemType.name}Edge`, fields: () => ({ node: { type: itemType }, cursor: { type: GraphQLString } }) }) } const PageType = (itemType) => { return new GraphQLObjectType({ // 动态生成唯一的Page类型名 name: `${itemType.name}Page`, fields: () => ({ totalCount: { type: GraphQLInt }, edges: { type: new GraphQLList(Edge(itemType)) }, pageInfo: { type: PageInfo } }) }) }
第二步:封装通用分页解析器,复用逻辑
在公共位置(可直接放在pagination.js中导出)编写通用分页逻辑生成函数,仅需要传入对应数据源,即可生成对应端点的resolve函数,避免重复编写分页代码:
// 放在pagination.js中,最后导出即可 function createPaginatedResolver(dataSource) { return (_, { first, afterCursor }) => { let afterIndex = 0 if (typeof afterCursor === 'string') { let nodeId = convertCursorToNodeId(afterCursor) let nodeIndex = dataSource.findIndex(item => item.id === nodeId) if (nodeIndex >= 0) { afterIndex = nodeIndex + 1 } } const slicedData = dataSource.slice(afterIndex, afterIndex + first) const edges = slicedData.map(node => ({ node, cursor: convertNodeToCursor(node) })) let startCursor = null let endCursor = null if (edges.length > 0) { startCursor = convertNodeToCursor(edges[0].node) endCursor = convertNodeToCursor(edges[edges.length - 1].node) } let hasNextPage = dataSource.length > afterIndex + first return { totalCount: dataSource.length, edges, pageInfo: { startCursor, endCursor, hasNextPage } } } } module.exports = { PageType, convertNodeToCursor, convertCursorToNodeId, createPaginatedResolver // 导出通用函数 }
第三步:在根查询中复用逻辑
修改RootQuery中的books字段,新增authors分页字段,直接调用通用函数生成resolve逻辑即可:
// 先导入createPaginatedResolver const { PageType, convertNodeToCursor, convertCursorToNodeId, createPaginatedResolver } = require('./pagination') const RootQuery = new GraphQLObjectType({ name: 'RootQueryType', description: 'this is the root query', fields: () => ({ // 原有单个book、author字段保持不变 book: { /* 原有逻辑 */ }, author: { /* 原有逻辑 */ }, // 复用分页逻辑的books端点 books: { type: PageType(Book), description: 'a list of books', args: { first: { type: GraphQLInt }, afterCursor: { type: GraphQLString } }, resolve: createPaginatedResolver(books) }, // 新增复用分页逻辑的authors端点 authors: { type: PageType(Author), description: 'a list of authors', args: { first: { type: GraphQLInt }, afterCursor: { type: GraphQLString } }, resolve: createPaginatedResolver(authors) } }) })
修改完成后即可同时支持两个端点的游标分页,不会再有类型重名报错,后续新增其他实体的分页能力仅需要调用PageType和createPaginatedResolver传入对应实体和数据源即可,无需重复编码。
内容的提问来源于stack exchange,提问作者dbzx10299
相关产品推荐
相关产品推荐

