pagination

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 11, 2026 License: MIT Imports: 4 Imported by: 0

README

pagination

import "github.com/brpaz/lib-go/pagination"

Package pagination provides types and helpers for offset-based and cursor-based pagination.

Offset pagination

OffsetPager carries page/pageSize input for SQL OFFSET/LIMIT queries. NewOffsetPager clamps both values to safe ranges (page ≥ 1, 1 ≤ pageSize ≤ MaxPageSize by default). Override the bounds per call site with WithDefaultPageSize / WithMaxPageSize — e.g. a higher cap for an internal API: pagination.NewOffsetPager(page, pageSize, pagination.WithMaxPageSize(500)). Page wraps the result set with total-count metadata:

pager := pagination.NewOffsetPager(page, pageSize) // from query params

var rows []User
var total int64
db.Model(&User{}).Count(&total).
    Offset(pager.Offset()).Limit(pager.Limit()).Find(&rows)

result := pagination.NewPage(rows, total, pager)
// result.TotalPages(), result.HasNext(), result.HasPrev()
Cursor pagination

CursorPager carries an opaque cursor token and a limit for keyset-style queries. NewCursorPager clamps the limit the same way NewOffsetPager clamps page size — override with WithDefaultLimit / WithMaxLimit. A cursor is one or more base64-encoded raw values — one per ORDER BY column, in column order — so multi-column sorts (e.g. created_at, id) stay stable across pages without skipping or repeating rows on ties. CursorPage wraps the result set with encoded next/prev cursor tokens:

pager := pagination.NewCursorPager(cursorParam, limitParam)

vals, err := pager.DecodedCursor() // []string{createdAt, id} for WHERE (created_at, id) > (?, ?)

// after fetching items, encode the boundary values in the same column order:
last, first := items[len(items)-1], items[0]
result := pagination.NewCursorPage(items,
    []string{last.CreatedAt.Format(time.RFC3339Nano), last.ID.String()},
    []string{first.CreatedAt.Format(time.RFC3339Nano), first.ID.String()},
)
// result.NextCursor, result.PrevCursor, result.HasNext, result.HasPrev

A single-column sort works the same way with one-element slices — pagination.NewCursorPage(items, []string{last.ID.String()}, []string{first.ID.String()}).

Index

Constants

const (
    DefaultPage     = 1
    DefaultPageSize = 20
    MaxPageSize     = 100
)

const DefaultCursorLimit = 20

func DecodeCursor

func DecodeCursor(cursor string) ([]string, error)

DecodeCursor decodes an opaque cursor token back to its raw values, in the same order they were passed to EncodeCursor.

func EncodeCursor

func EncodeCursor(values ...string) string

EncodeCursor encodes one or more raw values into a single opaque cursor token. Pass one value per ORDER BY column, in column order, to keep multi-column sorts stable across pages (e.g. EncodeCursor(createdAt, id)).

type CursorOption

CursorOption configures the bounds NewCursorPager clamps its limit against.

type CursorOption func(*cursorLimits)

func WithDefaultLimit
func WithDefaultLimit(n int) CursorOption

WithDefaultLimit overrides the limit used when the caller passes a value below 1 (e.g. an absent query parameter). Defaults to DefaultCursorLimit.

func WithMaxLimit
func WithMaxLimit(n int) CursorOption

WithMaxLimit overrides the upper bound the limit is clamped to. Defaults to MaxPageSize.

type CursorPage

CursorPage is a cursor-paginated result set.

type CursorPage[T any] struct {
    Items      []T
    NextCursor string
    PrevCursor string
    HasNext    bool
    HasPrev    bool
}

func NewCursorPage
func NewCursorPage[T any](items []T, nextVals, prevVals []string) CursorPage[T]

NewCursorPage builds a CursorPage. nextVals and prevVals are the raw cursor values of the last/first item — one per ORDER BY column, in the same order (e.g. []string{createdAt, id}). Pass nil (or empty) for nextVals/prevVals if there is no next/prev page.

type CursorPager

CursorPager holds cursor-based pagination input for repository queries.

type CursorPager struct {
    // Cursor is an opaque token pointing to the last seen item. Empty means start from beginning.
    Cursor string
    Limit  int
}

func NewCursorPager
func NewCursorPager(cursor string, limit int, opts ...CursorOption) CursorPager

NewCursorPager creates a CursorPager with a valid limit. By default the limit defaults to DefaultCursorLimit and is capped at MaxPageSize. Override either bound with WithDefaultLimit or WithMaxLimit — e.g. to allow larger pages on an internal API:

pager := pagination.NewCursorPager(cursor, limit, pagination.WithMaxLimit(500))

func (CursorPager) DecodedCursor
func (p CursorPager) DecodedCursor() ([]string, error)

DecodedCursor decodes the opaque cursor to its raw values (e.g. the last-seen sort-key values used to keep a multi-column ORDER BY stable, such as []string{createdAt, id}). Returns nil if cursor is empty (first page).

type OffsetOption

OffsetOption configures the bounds NewOffsetPager clamps page size against.

type OffsetOption func(*offsetLimits)

func WithDefaultPageSize
func WithDefaultPageSize(n int) OffsetOption

WithDefaultPageSize overrides the page size used when the caller passes a value below 1 (e.g. an absent query parameter). Defaults to DefaultPageSize.

func WithMaxPageSize
func WithMaxPageSize(n int) OffsetOption

WithMaxPageSize overrides the upper bound page size is clamped to. Defaults to MaxPageSize.

type OffsetPager

OffsetPager holds offset-based pagination input for repository queries.

type OffsetPager struct {
    Page     int
    PageSize int
}

func NewOffsetPager
func NewOffsetPager(page, pageSize int, opts ...OffsetOption) OffsetPager

NewOffsetPager creates an OffsetPager, clamping values to valid ranges. By default page defaults to DefaultPage, pageSize to DefaultPageSize, and is capped at MaxPageSize. Override either bound with WithDefaultPageSize or WithMaxPageSize — e.g. to allow larger pages on an internal API:

pager := pagination.NewOffsetPager(page, pageSize, pagination.WithMaxPageSize(500))

func (OffsetPager) Limit
func (p OffsetPager) Limit() int

Limit returns the DB limit (same as PageSize).

func (OffsetPager) Offset
func (p OffsetPager) Offset() int

Offset returns the DB offset for this page.

type Page

Page is a paginated result set for offset-based pagination.

type Page[T any] struct {
    Items      []T
    Total      int64
    PageNumber int
    PageSize   int
}

func NewPage
func NewPage[T any](items []T, total int64, pager OffsetPager) Page[T]

NewPage creates a Page from query results and the originating OffsetPager.

func (Page[T]) HasNext
func (p Page[T]) HasNext() bool

HasNext reports whether a next page exists.

func (Page[T]) HasPrev
func (p Page[T]) HasPrev() bool

HasPrev reports whether a previous page exists.

func (Page[T]) TotalPages
func (p Page[T]) TotalPages() int

TotalPages returns the number of pages for the total count.

Generated by gomarkdoc

Documentation

Overview

Package pagination provides types and helpers for offset-based and cursor-based pagination.

Offset pagination

OffsetPager carries page/pageSize input for SQL OFFSET/LIMIT queries. NewOffsetPager clamps both values to safe ranges (page ≥ 1, 1 ≤ pageSize ≤ MaxPageSize by default). Override the bounds per call site with WithDefaultPageSize / WithMaxPageSize — e.g. a higher cap for an internal API: pagination.NewOffsetPager(page, pageSize, pagination.WithMaxPageSize(500)). Page wraps the result set with total-count metadata:

pager := pagination.NewOffsetPager(page, pageSize) // from query params

var rows []User
var total int64
db.Model(&User{}).Count(&total).
    Offset(pager.Offset()).Limit(pager.Limit()).Find(&rows)

result := pagination.NewPage(rows, total, pager)
// result.TotalPages(), result.HasNext(), result.HasPrev()

Cursor pagination

CursorPager carries an opaque cursor token and a limit for keyset-style queries. NewCursorPager clamps the limit the same way NewOffsetPager clamps page size — override with WithDefaultLimit / WithMaxLimit. A cursor is one or more base64-encoded raw values — one per ORDER BY column, in column order — so multi-column sorts (e.g. created_at, id) stay stable across pages without skipping or repeating rows on ties. CursorPage wraps the result set with encoded next/prev cursor tokens:

pager := pagination.NewCursorPager(cursorParam, limitParam)

vals, err := pager.DecodedCursor() // []string{createdAt, id} for WHERE (created_at, id) > (?, ?)

// after fetching items, encode the boundary values in the same column order:
last, first := items[len(items)-1], items[0]
result := pagination.NewCursorPage(items,
    []string{last.CreatedAt.Format(time.RFC3339Nano), last.ID.String()},
    []string{first.CreatedAt.Format(time.RFC3339Nano), first.ID.String()},
)
// result.NextCursor, result.PrevCursor, result.HasNext, result.HasPrev

A single-column sort works the same way with one-element slices — pagination.NewCursorPage(items, []string{last.ID.String()}, []string{first.ID.String()}).

Index

Constants

View Source
const (
	DefaultPage     = 1
	DefaultPageSize = 20
	MaxPageSize     = 100
)
View Source
const DefaultCursorLimit = 20

Variables

This section is empty.

Functions

func DecodeCursor

func DecodeCursor(cursor string) ([]string, error)

DecodeCursor decodes an opaque cursor token back to its raw values, in the same order they were passed to EncodeCursor.

func EncodeCursor

func EncodeCursor(values ...string) string

EncodeCursor encodes one or more raw values into a single opaque cursor token. Pass one value per ORDER BY column, in column order, to keep multi-column sorts stable across pages (e.g. EncodeCursor(createdAt, id)).

Types

type CursorOption

type CursorOption func(*cursorLimits)

CursorOption configures the bounds NewCursorPager clamps its limit against.

func WithDefaultLimit

func WithDefaultLimit(n int) CursorOption

WithDefaultLimit overrides the limit used when the caller passes a value below 1 (e.g. an absent query parameter). Defaults to DefaultCursorLimit.

func WithMaxLimit

func WithMaxLimit(n int) CursorOption

WithMaxLimit overrides the upper bound the limit is clamped to. Defaults to MaxPageSize.

type CursorPage

type CursorPage[T any] struct {
	Items      []T
	NextCursor string
	PrevCursor string
	HasNext    bool
	HasPrev    bool
}

CursorPage is a cursor-paginated result set.

func NewCursorPage

func NewCursorPage[T any](items []T, nextVals, prevVals []string) CursorPage[T]

NewCursorPage builds a CursorPage. nextVals and prevVals are the raw cursor values of the last/first item — one per ORDER BY column, in the same order (e.g. []string{createdAt, id}). Pass nil (or empty) for nextVals/prevVals if there is no next/prev page.

type CursorPager

type CursorPager struct {
	// Cursor is an opaque token pointing to the last seen item. Empty means start from beginning.
	Cursor string
	Limit  int
}

CursorPager holds cursor-based pagination input for repository queries.

func NewCursorPager

func NewCursorPager(cursor string, limit int, opts ...CursorOption) CursorPager

NewCursorPager creates a CursorPager with a valid limit. By default the limit defaults to DefaultCursorLimit and is capped at MaxPageSize. Override either bound with WithDefaultLimit or WithMaxLimit — e.g. to allow larger pages on an internal API:

pager := pagination.NewCursorPager(cursor, limit, pagination.WithMaxLimit(500))

func (CursorPager) DecodedCursor

func (p CursorPager) DecodedCursor() ([]string, error)

DecodedCursor decodes the opaque cursor to its raw values (e.g. the last-seen sort-key values used to keep a multi-column ORDER BY stable, such as []string{createdAt, id}). Returns nil if cursor is empty (first page).

type OffsetOption

type OffsetOption func(*offsetLimits)

OffsetOption configures the bounds NewOffsetPager clamps page size against.

func WithDefaultPageSize

func WithDefaultPageSize(n int) OffsetOption

WithDefaultPageSize overrides the page size used when the caller passes a value below 1 (e.g. an absent query parameter). Defaults to DefaultPageSize.

func WithMaxPageSize

func WithMaxPageSize(n int) OffsetOption

WithMaxPageSize overrides the upper bound page size is clamped to. Defaults to MaxPageSize.

type OffsetPager

type OffsetPager struct {
	Page     int
	PageSize int
}

OffsetPager holds offset-based pagination input for repository queries.

func NewOffsetPager

func NewOffsetPager(page, pageSize int, opts ...OffsetOption) OffsetPager

NewOffsetPager creates an OffsetPager, clamping values to valid ranges. By default page defaults to DefaultPage, pageSize to DefaultPageSize, and is capped at MaxPageSize. Override either bound with WithDefaultPageSize or WithMaxPageSize — e.g. to allow larger pages on an internal API:

pager := pagination.NewOffsetPager(page, pageSize, pagination.WithMaxPageSize(500))

func (OffsetPager) Limit

func (p OffsetPager) Limit() int

Limit returns the DB limit (same as PageSize).

func (OffsetPager) Offset

func (p OffsetPager) Offset() int

Offset returns the DB offset for this page.

type Page

type Page[T any] struct {
	Items      []T
	Total      int64
	PageNumber int
	PageSize   int
}

Page is a paginated result set for offset-based pagination.

func NewPage

func NewPage[T any](items []T, total int64, pager OffsetPager) Page[T]

NewPage creates a Page from query results and the originating OffsetPager.

func (Page[T]) HasNext

func (p Page[T]) HasNext() bool

HasNext reports whether a next page exists.

func (Page[T]) HasPrev

func (p Page[T]) HasPrev() bool

HasPrev reports whether a previous page exists.

func (Page[T]) TotalPages

func (p Page[T]) TotalPages() int

TotalPages returns the number of pages for the total count.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL