Bind backward pagination cursors to request context
Safely paginate older records with a descending lookahead and chronological response when continuation tokens are user-controlled and UI context can change.
Bind backward pagination cursors to request context
Use this when an API selects older records in descending order with a one-row lookahead, returns each page chronologically, and accepts a user-controlled continuation token. Assume a unique stable ordering key; otherwise use a stable composite key in both the query and cursor.
Preserve the page boundary
Fetch the requested page size plus one in descending order. The extra oldest row establishes whether another page exists but is not returned. Reverse the retained rows for presentation. When another page exists, the next cursor carries the oldest retained ordering key, not the lookahead row. The following query uses a strict older-than predicate.
Issue an opaque authenticated cursor
Authorize the initial request, normalize every query-affecting filter and default, and choose a server-issued snapshot. Store the authorization scope, resource binding, canonical filter fingerprint, direction, snapshot reference, retained boundary key, token version, and expiry in server-side cursor state. Give the client a random handle with a server message authentication code. The handle reveals no internal resource or row identifiers. A standard authenticated-encrypted stateless token is an alternative, but a plain signed readable payload is not confidential.
Bind the page size too if changing it during traversal is disallowed. Sign with a managed key and allow bounded key rotation. Limit token length and lifetime. A token is a continuation hint, never an authorization grant.
Validate before any page query
Authenticate the caller and reauthorize access to the requested resource. Check token format, authentication tag, version, and expiry; then compare its bound scope, resource, normalized filters, direction, page-size policy, and snapshot with the current request. Reject a mismatch or invalid token using one generic invalid-cursor response. Do not echo the token, internal identifiers, or which comparison failed. Do not build a data query from unverified fields.
The client should clear its cursor when the selected resource, filters, or direction changes. If an old cursor is nonetheless sent and rejected, restart from a fresh first page and snapshot rather than silently repurposing it.
Define snapshot semantics explicitly
For immutable append-only records with immutable filter membership, a first-page high-water ordering key can exclude later inserts. It is not a full snapshot if records may be edited, deleted, or move into or out of filters. In that case use a database-supported as-of version or equivalent immutable view, and reject expired snapshot references.
Basis
This combines the exclusive older-than ordering invariant with a trust-boundary analysis of user-controlled cursors. It is design reasoning only; no code was run or inspected and no runtime tests were executed.