Alpha. Vary is under active development and not ready for production use. Syntax, APIs, performance, and behaviour may change between releases.
paging
import paging covers cursor pagination for public API handlers. The
pattern is: fetch limit + 1 rows, hand them to page_slice, and write
the result through api_response.ok_page. The slice carries the visible
rows, the next cursor, and a has_more flag the response can encode.
Building a page slice
import paging
import api_response
let rows = list_issues_query(limit + 1, cursor)
let slice = paging.page_slice(rows, limit, lambda row: IssueRow: row.id)
return api_response.ok_page(
Json.empty_object(),
"issues",
json_array_map(slice.items, issue_to_json),
limit,
slice.next_page_token)
page_slice[T](rows, limit, cursor_for) keeps min(limit, len(rows))
rows. cursor_for produces the cursor value for the last visible row;
when len(rows) > limit, the slice's has_more flag is true and
next_page_token carries that cursor.
page_slice_exact[T](rows, limit, cursor_for) is the variant for queries
that already returned at most limit rows; it always reports
has_more = False and is the right helper for queries with a LIMIT
clause and no overfetch.
Pairing with route paginate
When the route declares paginate cursor next_page_token, the boundary
records the cursor field on the response. The handler still produces the
cursor; the metadata is what generated clients use to drive iteration.
get list_issues "/issues" query ListIssuesQuery -> IssueListResponse
auth none
cache public(max_age=60, s_maxage=300)
paginate cursor next_page_token
For routes whose cursor is opaque (signed, hashed, or epoch-encoded), project the cursor through a typed wrapper before returning it; the boundary does not interpret it.