Skip to main content

Pagination

Hysteria ORM provides two pagination strategies for efficient data retrieval: offset-based pagination (paginate) and cursor-based pagination (paginateWithCursor).

Offset-Based Pagination

paginate(page, perPage)

Returns a page of results along with metadata about the total result set.

const result = await sql.from(User).paginate(1, 10);

console.log(result.data); // User[]
console.log(result.paginationMetadata);
// {
// total: 100,
// perPage: 10,
// currentPage: 1,
// firstPage: 1,
// isEmpty: false,
// lastPage: 10,
// hasMorePages: true,
// hasPages: true,
// }

You can combine it with any query builder method:

const result = await sql
.from(User)
.where("status", "active")
.orderBy("createdAt", "desc")
.select("id", "name", "email")
.paginate(2, 25);

Pagination Metadata

The paginationMetadata object contains:

PropertyTypeDescription
totalnumberTotal number of matching records
perPagenumberNumber of records per page
currentPagenumberCurrent page number
firstPagenumberAlways 1
isEmptybooleantrue if total is 0
lastPagenumberLast page number (minimum 1)
hasMorePagesbooleantrue if there are pages after the current
hasPagesbooleantrue if total exceeds perPage

Raw Query Builder

The raw query builder also supports pagination:

const result = await sql.from("users").paginate(1, 10);
console.log(result.data); // Record<string, any>[]
console.log(result.paginationMetadata);

Cursor-Based Pagination

paginateWithCursor(limit, options, cursor?)

Cursor-based pagination is ideal for infinite-scroll UIs or real-time feeds where items may be inserted between page loads. Instead of using an offset, it uses a discriminator column value to determine where the next page starts.

// First page
const [firstPage, cursor] = await sql.from(User).paginateWithCursor(10, {
discriminator: "id",
orderBy: "asc",
});

console.log(firstPage.data); // User[]
console.log(cursor); // { key: "id", value: 42 }

// Next page — pass the cursor from the previous response
const [nextPage, nextCursor] = await sql.from(User).paginateWithCursor(
10,
{
discriminator: "id",
orderBy: "asc",
},
cursor,
);

Cursor Options

OptionTypeDescription
discriminatorkeyof ModelColumn to paginate on (usually the primary key)
orderBy"asc" | "desc"Sort direction (default: "asc")
operator">" | "<" etc.Comparison operator for cursor (default: ">")

Cursor Pagination Metadata

The cursor pagination metadata is simpler:

PropertyTypeDescription
totalnumberTotal number of matching records
perPagenumberNumber of records per page
firstPagenumberAlways 1
isEmptybooleantrue if total is 0

Chunking Large Datasets

The chunk method processes large datasets in manageable pieces without loading everything into memory:

for await (const users of sql.from(User).chunk(250)) {
await processUserBatch(users);
}

Choosing a Strategy

StrategyBest ForProsCons
paginateNumbered pages, admin dashboardsSimple API, total count, page metadataSlower on very deep offsets¹
paginateWithCursorInfinite scroll, real-time feedsConsistent performance at any depthNo random page access, no total pages
chunkBatch processing, data exportsMemory efficientNot for API responses