You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 06:15:04