← 中文资源

简体中文版本

GraphQL 游标分页拉取

用 graphql-request 消费 Relay 风格连接分页:沿 edges 的 cursor 与 pageInfo 推进请求,直到 hasNextPage 为假取尽数据。

阅读英文原文

示例说明

Relay 连接规范把分页元数据内嵌到响应里:edges 携带每条数据的游标,pageInfo 的 hasNextPage 与 endCursor 告诉调用方是否还有下一页以及从哪里继续。相比 offset 分页,游标在数据插入删除后依然稳定,不会漏页或重页。这个封装把翻页循环收敛成一个返回全量的异步函数。

技术要点

  • endCursor 是下一页的起点游标
  • hasNextPage 决定循环终止条件
  • 变量定义让查询可被服务端缓存复用

代码解读

查询把 first 与 after 声明为 GraphQL 变量而不是拼进查询文本,同一文档可复用;fetchAllOrders 用 while 循环不断以 endCursor 作为下一轮 after 值请求,把 edges 的 node 展平累积到结果数组,hasNextPage 为假时跳出循环。

使用建议

全量拉取仅适合离线同步任务,在线列表应该单页渲染与增量加载。pageSize 过大会加大单次响应延迟与内存峰值,按下游承受力调整。请求失败时应保留已完成页,断点续传从上个游标继续。

代码示例

import { GraphQLClient, gql } from 'graphql-request';

const client = new GraphQLClient('https://api.example.com/graphql', {
  headers: { Authorization: 'Bearer ' + process.env.API_TOKEN },
});

const query = gql`
  query Orders($first: Int!, $after: String) {
    orders(first: $first, after: $after) {
      edges { cursor node { id total } }
      pageInfo { hasNextPage endCursor }
    }
  }
`;

async function fetchAllOrders(pageSize = 50) {
  const results = [];
  let after = null;

  while (true) {
    const data = await client.request(query, { first: pageSize, after });
    const { edges, pageInfo } = data.orders;
    results.push(...edges.map((edge) => edge.node));

    if (!pageInfo.hasNextPage) break;
    after = pageInfo.endCursor;
  }
  return results;
}

const orders = await fetchAllOrders();
console.log('total orders fetched:', orders.length);