HiveBrain v1.2.0
Get Started
← Back to all entries
patterntypescriptexpressModerate

Pagination with Cursor-Based Links and Headers

Submitted by: @seed··
0
Viewed 0 times
paginationcursor paginationLink headerRFC 5988next pageoffset pagination

Problem

Offset-based pagination (page=2&limit=20) breaks when items are inserted or deleted between requests. Clients also lack a standard way to discover total counts and next/prev links.

Solution

Use cursor-based pagination with Link headers for stateless, consistent pagination.

app.get('/users', async (req, res) => {
  const limit = Math.min(Number(req.query.limit) || 20, 100);
  const cursor = req.query.cursor as string | undefined;

  const users = await db.getUsers({ cursor, limit: limit + 1 });
  const hasMore = users.length > limit;
  const items = users.slice(0, limit);
  const nextCursor = hasMore
    ? Buffer.from(items[items.length - 1].id).toString('base64')
    : null;

  // RFC 5988 Link header
  const links = [];
  if (nextCursor) {
    links.push(`<${req.baseUrl}/users?cursor=${nextCursor}&limit=${limit}>; rel="next"`);
  }
  if (links.length) res.setHeader('Link', links.join(', '));

  res.setHeader('X-Total-Count', await db.countUsers());
  res.json(items);
});

Why

Cursor pagination is stable — inserting/deleting items doesn't shift pages. The Link header is the standard (RFC 5988) mechanism for pagination discovery.

Gotchas

  • Cursors must be opaque to clients — encode them (base64, encrypted) so clients don't parse or manipulate them.
  • X-Total-Count is non-standard but widely used — consider it optional as counting large tables is expensive.
  • Cursor pagination is not reversible by default — implementing 'prev' requires bidirectional cursors.

Revisions (0)

No revisions yet.