Skip to content

after 参数参考

after (GraphQL) / $after (REST) 参数用于游标分页的翻页——传入上一页返回的游标令牌,获取下一页数据。与页码分页不同,游标分页在数据变动时不会重复或遗漏记录。

REST:$after

语法

?$after={cursor-token}

cursor-token 是上一页响应中 nextLink 包含的游标值。客户端不需要解析其内容——它是不透明的 Base64 编码字符串。

示例

bash
# 第一页
curl "http://localhost:5000/api/Book?\$first=5"

响应:

json
{
  "value": [ ... ],
  "nextLink": "http://localhost:5000/api/Book?$first=5&$after=eyJpZCI6NX0="
}

nextLink 中提取 $after 值用于下一页:

bash
# 第二页
curl "http://localhost:5000/api/Book?\$first=5&\$after=eyJpZCI6NX0="

遍历全量数据

javascript
async function fetchAllPages(baseUrl) {
  let url = `${baseUrl}/api/Book?$first=20`;
  const allItems = [];
  while (url) {
    const res = await fetch(url);
    const data = await res.json();
    allItems.push(...data.value);
    url = data.nextLink || null;
  }
  return allItems;
}

行为规则

规则说明
必须与 $first 配合单独传 $after 时使用默认分页大小
nextLink 可能不存在不传 $first 且结果未超过默认大小时,无 nextLink
游标不可跳页只能跟随 nextLink 逐页访问,不能跳页
游标为不透明值客户端不应解析或构造游标值

GraphQL:after

语法

graphql
{
  books(first: {integer}, after: "{cursor}") {
    items { ... }
    hasNextPage
    endCursor
  }
}

示例

graphql
# 第一页
{
  books(first: 5) {
    items { id title }
    hasNextPage
    endCursor
  }
}

响应:

json
{
  "data": {
    "books": {
      "items": [ ... ],
      "hasNextPage": true,
      "endCursor": "eyJpZCI6NX0="
    }
  }
}

endCursor 传给下一页的 after

graphql
# 第二页
{
  books(first: 5, after: "eyJpZCI6NX0=") {
    items { id title }
    hasNextPage
    endCursor
  }
}

变量形式

graphql
query GetBooks($after: String) {
  books(first: 20, after: $after) {
    items { id title }
    hasNextPage
    endCursor
  }
}

遍历全量数据

javascript
async function fetchAllPages(gqlUrl) {
  const allItems = [];
  let cursor = null, hasNext = true;
  while (hasNext) {
    const res = await fetch(gqlUrl, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        query: `query($after: String) { books(first: 20, after: $after) { items { id title } hasNextPage endCursor } }`,
        variables: { after: cursor }
      })
    });
    const page = (await res.json()).data.books;
    allItems.push(...page.items);
    hasNext = page.hasNextPage;
    cursor = page.endCursor;
  }
  return allItems;
}

游标分页原理

游标分页通过记录"上一页最后一条记录的位置"来实现翻页。相比页码分页(page=1, page=2),游标分页的优势:

特性游标分页页码分页
数据变动时的一致性不会重复或遗漏可能重复或遗漏
跳页不支持支持
大偏移量性能始终高效偏移量大时性能下降
实现复杂度低(跟随 nextLink/endCursor)

数据墙DBW 选择游标分页是因为它在数据库层面表现更好(基于索引的游标比 OFFSET 更高效),且插入或删除中间数据时不会导致翻页结果错乱。

REST vs GraphQL 对照

操作RESTGraphQL
翻页参数?$after={token}after: "{token}"
获取下一页令牌响应 nextLink 中包含响应 endCursor 字段
检测是否有下一页nextLink 是否存在hasNextPage: true/false
令牌格式Base64 编码Base64 编码

限定

限定说明
不能跳页只能跟随游标逐页访问
存储过程不支持分页和 $after/after
聚合查询不支持分页

下一步

  • first — 分页大小参数参考。
  • filter — 条件筛选参数参考。
  • orderby — 排序参数参考。

数据墙DBW 产品文档与开发指南。