Core API Reference
Every public function and constant in @evensurface/paginate, with inputs and outputs.
This is the lower-level core API, intended for custom data sources and advanced integrations. If you use Drizzle, start with the Quick Start or Drizzle API instead. Its paginate() function accepts Drizzle queries directly and does not require an adapter argument.
This page covers the public JavaScript functions exported by @evensurface/paginate. You import them from the package root:
import {
paginate,
paginateOffset,
paginateCursor,
parsePage,
parseLimit,
} from "@evensurface/paginate"You don't have to use the helper functions if you call paginate(), paginateOffset(), or paginateCursor()—those functions already parse inputs and create metadata for you.
Main pagination functions
paginate()
Use this for numbered pages. It counts matching rows, resolves the requested page, calculates an offset, and fetches that page.
- Input:
PaginationOptions<TQuery, TCountQuery, TItem>. Required:adapter,query,countQuery. Optional:page,limit,pageSizeOptions. - Output:
Promise<PaginatedResult<TItem>>withitemsandPaginationMetadata.
const result = await paginate({
adapter, // Your adapter's count() and fetch() methods
query: { active: true }, // Filter sent to fetch()
countQuery: { active: true }, // Filter sent to count()
page: 2, // Request page 2 (first page is 1)
limit: 20, // At most 20 rows per page
})Here adapter is a custom adapter you've already created (see Create Your Own Adapter). If the count is 95, the metadata has page: 2, total: 95, offset: 20, and totalPages: 5. See the Page Guide for the complete output object.
paginateOffset()
Use this when the caller provides a number of rows to skip instead of a page number. The offset isn't clamped to the last page.
- Input:
OffsetPaginationOptions<TQuery, TCountQuery, TItem>. Required:adapter,query,countQuery. Optional:offset,limit,pageSizeOptions. - Output:
Promise<OffsetPaginatedResult<TItem>>withitemsandOffsetPaginationMetadata.
const result = await paginateOffset({
adapter,
query: { active: true },
countQuery: { active: true },
offset: 40, // Skip 40 matching rows
limit: 20, // Fetch up to 20 more
})If the count is 95, it requests matching positions 41–60 and returns metadata including { total: 95, limit: 20, offset: 40 }. See Offset Pagination for all fields and the beyond-the-end case.
paginateCursor()
Use this to fetch the next batch in a feed. It does not count rows and does not use a numeric offset.
- Input:
CursorPaginationOptions<TQuery, TItem>. Required:adapter,query. Optional:cursor,limit,pageSizeOptions. - Output:
Promise<CursorPaginatedResult<TItem>>withitemsandCursorPaginationMetadata.
const result = await paginateCursor({
adapter: cursorAdapter,
query: { published: true },
limit: 20,
// cursor: previousResult.pagination.nextCursor ?? undefined
})cursorAdapter is a cursor adapter that fetches rows after a sort position and implements getPosition() (see the complete cursor example). Paginate asks the adapter for 21 rows to detect more results, but returns at most 20. When a further batch exists, the output contains hasNextPage: true and a generated nextCursor string.
Input helpers
These functions take raw request inputs and return normalized numbers. They do not fetch data.
parsePage(value?)
- Input:
value?:PaginationInput(string | number). - Output:
number(a positive safe integer). - Rule: Valid positive whole numbers pass through. Missing or invalid values become
1.
parsePage("3") // 3
parsePage(2) // 2
parsePage("abc") // 1
parsePage(0) // 1
parsePage("2.5") // 1parseOffset(value?)
- Input:
value?:PaginationInput. - Output:
number(a non-negative safe integer). - Rule: Valid offsets pass through, including zero. Missing, negative, fractional, or invalid values become
0.
parseOffset("40") // 40
parseOffset(0) // 0
parseOffset("-4") // 0
parseOffset("no") // 0parseLimit(value?, options?)
- Input:
value?:PaginationInput;options?:PageSizeOptions. - Output:
number: a positive safe integer inside the allowed range. - Rule: Missing or invalid values use
defaultLimit(normally 20); valid values smaller thanminLimitare raised, and values greater thanmaxLimitare reduced.
parseLimit(undefined) // 20: default
parseLimit(0) // 20: invalid input uses default
parseLimit("25") // 25
parseLimit(500) // 100: default maximum
parseLimit(3, { defaultLimit: 10, minLimit: 5, maxLimit: 50 }) // 5The configuration itself must be valid: minLimit, defaultLimit, and maxLimit must be positive safe integers, and minLimit <= defaultLimit <= maxLimit. Otherwise the helper throws a coded RangeError. It doesn't silently repair your configuration.
Page and offset math helpers
These helpers take already validated numeric values. Unlike parsePage() and parseOffset(), they throw for invalid inputs.
calculateOffset(page, limit)
- Input:
page: numberandlimit: number, both positive safe integers. - Output:
number(the zero-based offset). - Calculation:
(page - 1) × limit.
calculateOffset(1, 20) // 0: first page starts immediately
calculateOffset(2, 20) // 20: skip 20 matching rows
calculateOffset(5, 10) // 40Throws INVALID_PAGE or INVALID_LIMIT if the inputs are invalid; throws OFFSET_OVERFLOW if their product isn't a safe integer.
resolvePage(page, totalPages)
- Input:
page: number(positive safe integer),totalPages: number(non-negative safe integer). - Output:
number, the page that should actually be used. - Rule: If the requested page is higher than
totalPages, use the last page. IftotalPagesis zero, return page1.
resolvePage(3, 10) // 3: page exists
resolvePage(15, 10) // 10: last available page
resolvePage(5, 0) // 1: empty datasetThrows INVALID_PAGE or INVALID_TOTAL_PAGES for invalid values.
Metadata helpers
These create metadata without querying any database. Use them if you already know the count and want the same format that the pagination functions would return.
createPaginationMetadata(options)
- Input:
PaginationMetadataOptions:{ page: number; limit: number; total: number }. - Output:
PaginationMetadata.
const metadata = createPaginationMetadata({
page: 2, // Requested page
limit: 20, // Maximum items per page
total: 95, // Total matching rows (already known)
})
// {
// page: 2, limit: 20, offset: 20, total: 95,
// totalPages: 5, hasNextPage: true, hasPreviousPage: true,
// firstItem: 21, lastItem: 40,
// }The function clamps an out-of-range page before calculating its offset. Invalid totals, pages, or limits throw coded errors.
createOffsetPaginationMetadata(options)
- Input:
OffsetPaginationMetadataOptions:{ offset: number; limit: number; total: number }. - Output:
OffsetPaginationMetadata.
const metadata = createOffsetPaginationMetadata({
offset: 40, // Rows skipped
limit: 20, // Rows requested
total: 95, // Total matches
})
// { total: 95, limit: 20, offset: 40,
// hasNextPage: true, hasPreviousPage: true }Unlike numbered-page metadata, this helper preserves the offset, even if it's beyond the end. The inputs are strict numbers: offset and total must be non-negative safe integers; limit must be positive.
createPageWindow(options)
Use this to generate page-number controls for a UI. It doesn't fetch items or return a React component.
- Input:
PageWindowOptions:{ page, totalPages, siblings?, boundaries? }. - Output:
PaginationPageItem[], an array of page numbers and"ellipsis"markers.
const buttons = createPageWindow({
page: 5, // Selected page
totalPages: 20, // Available pages
siblings: 1, // Pages beside current page (default 1)
boundaries: 1, // Pages at each end (default 1)
})
// [1, "ellipsis", 4, 5, 6, "ellipsis", 20]Render a number as a link and "ellipsis" as a non-clickable gap. If there are no pages (totalPages: 0), it returns []. For an illustrated explanation and a React example, see Build page-number buttons.
Error helper
isPaginationError(error)
- Input:
unknown(usually the caught value from atry/catch). - Output:
booleanat runtime. In TypeScript it narrows the value toPaginationErrorwhen it returnstrue.
import { isPaginationError } from "@evensurface/paginate"
try {
parseLimit(5, { defaultLimit: 20, minLimit: 30, maxLimit: 100 })
} catch (error) {
if (isPaginationError(error)) {
console.log(error.code) // "INVALID_DEFAULT_LIMIT"
}
}This guard identifies core-authored errors, not arbitrary database or Drizzle adapter errors. See Errors.
Exported constants
| Constant | Type | Value | Meaning |
|---|---|---|---|
DEFAULT_PAGE_SIZE | number | 20 | Default limit when none is valid. |
MIN_PAGE_SIZE | number | 1 | Default smallest allowed limit. |
MAX_PAGE_SIZE | number | 100 | Default largest allowed limit. |
import { DEFAULT_PAGE_SIZE, MIN_PAGE_SIZE, MAX_PAGE_SIZE } from "@evensurface/paginate"
// 20, 1, 100Want the full interfaces? Visit the Core Type Reference. Using Drizzle? See the separate Drizzle API Reference.