API Design

Stable Cursor Pagination Without Duplicates or Gaps During Changes

Paginate activity with immutable sort keys, unique tie-breakers, signed cursors, direction semantics, and clear behavior under equal timestamps, deletion, filters, and snapshots.

A sound implementation does not ask users to refresh until it works. Each phase has an input, an output, a timeout, and a terminal state. Offset shifts after inserts and creates duplicates or gaps. A cursor encodes the final ordering tuple such as createdAt and ID and queries with strict comparison.

An API contract includes units, defaults, idempotency, error classes, and version windows—not only field types. Clients distinguish retryable, user-action, and permanent outcomes to avoid duplicate side effects.

Define the system contract first

This capability crosses clients, networks, and servers, so a local optimization can create a system failure. Decisions must constrain both endpoints, persisted truth, and operating budgets together.

  • Use immutable order plus unique ID, version and sign cursors bound to filters, and return next/prev plus hasMore without exposing mutable internals.
  • Bound every input by size, count, and time, returning a stable actionable error code when a budget is exceeded.
  • Use explicit capability negotiation so older clients receive an explained fallback instead of a half-working state.

The delivery standard for Stable Cursor Pagination Without Duplicates or Gaps During Changes is a usable normal path, convergent failures, bounded resources, and a state users can understand. The result is a production capability that can be explained, degraded safely, and rolled back—not a demo that works once.

What can break that contract

Failure and success must share one state model. An error toast that neither releases resources nor propagates a terminal state leaves dirty work for the next recovery attempt.

  • Second-resolution createdAt skips tied records, while a cursor unbound from actor and filters can probe another dataset.
  • A stale response arriving after a new task can overwrite healthy state or restart cancelled work without version fencing.
  • An untested fallback receives all traffic during a primary failure and becomes the slower, more expensive bottleneck.

How to test the contract line by line

Do not stop verification when the final action succeeds. Count side effects, measure wait time, inspect privacy, and prove the next run begins from a clean baseline.

  1. Insert, delete, and modify around every boundary during forward and reverse scans; results must avoid duplicates under documented snapshot or live semantics.
  2. Race refresh, cancel, timeout, and remote completion in one scheduling window; assert one terminal state and one side effect.
  3. Use fault injection to prove alerts precede user reports and operators can locate the failing phase from bounded evidence.

A capability becomes maintainable when it degrades safely, repetition adds no side effects, and its signals reveal a fault before user reports do.

Put the guide to work

Open uCopy and connect two devices securely from the browser.

Start for free