Skip to content

Cursor-Based Pagination — Stable Pointers for Dynamic Data

DodaTech Updated 2026-06-28 2 min read

In this tutorial, you will learn about Cursor. We cover key concepts, practical examples, and best practices to help you master this topic.

Cursor-based pagination uses an opaque token (cursor) that points to a specific record, providing consistent pagination results even when new data is inserted or existing data is deleted.

What You'll Learn

You will learn cursor-based pagination implementation using encoded cursors, database queries with Composite keys, and when to prefer cursors over offsets.

Request Format

GET /api/users?cursor=eyJpZCI6IDIwfQ==&limit=20

Server Implementation

app.get("/api/users", async (req, res) => {
  const limit = Math.min(parseInt(req.query.limit) || 20, 100);
  const cursor = req.query.cursor
    ? JSON.parse(Buffer.from(req.query.cursor, "base64").toString())
    : null;

  let query;
  let params;

  if (cursor) {
    query = `
      SELECT * FROM users
      WHERE (created_at, id) < ($1, $2)
      ORDER BY created_at DESC, id DESC
      LIMIT $3
    `;
    params = [cursor.createdAt, cursor.id, limit];
  } else {
    query = `
      SELECT * FROM users
      ORDER BY created_at DESC, id DESC
      LIMIT $1
    `;
    params = [limit];
  }

  const result = await db.query(query, params);
  const users = result.rows;

  let nextCursor = null;
  if (users.length === limit) {
    const last = users[users.length - 1];
    nextCursor = Buffer.from(
      JSON.stringify({ createdAt: last.created_at, id: last.id })
    ).toString("base64");
  }

  res.json({
    data: users,
    pagination: { limit, nextCursor, hasMore: users.length === limit }
  });
});

SQL Pattern

-- First page
SELECT * FROM users ORDER BY created_at DESC, id DESC LIMIT 20;

-- Subsequent pages
SELECT * FROM users
WHERE (created_at, id) < ($1, $2)
ORDER BY created_at DESC, id DESC
LIMIT 20;

Performance

Cursor pagination scans only the requested rows (plus LIMIT), unlike offset pagination that scans all skipped rows. This is significantly faster at high page numbers.

Common Mistakes

  1. Reversible cursors — Using easily guessed cursor values. Encode with base64 or encryption.
  2. Non-unique sort keys — Using sort keys that allow multiple records at the same position.
  3. No hasMore flag — Clients cannot tell if there are more pages.

Practice Questions

  1. How is a cursor generated?
  2. Why is cursor pagination more consistent than offset?
  3. What SQL pattern does cursor pagination use?
  4. How do you encode cursor values?

Challenge

Implement cursor-based pagination for a real-time event feed. Events are inserted continuously. Use composite cursor with timestamp and ID.

FAQ

Can users jump to arbitrary pages with cursor pagination?

No. Cursor pagination only supports next/previous navigation. Users cannot skip directly to page 5.

How do I handle backward pagination?

Generate a second cursor for backward navigation. Use > instead of < in the WHERE clause.

Is base64 encoding secure enough?

Base64 is encoding, not encryption. Use it for opaque tokens. Do not put sensitive data in cursors.

What's Next

In the next lesson, you will learn keyset pagination.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro