pagination

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Nov 6, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package pagination implements bounded offset and authenticated cursor pagination.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CalculateOffset

func CalculateOffset(p Offset) (int, error)

CalculateOffset returns a validated zero-based SQL offset.

func OffsetSQL

func OffsetSQL(dialect, template string, args []any, p Offset, options ...OffsetConfig) (string, []any, error)

OffsetSQL expands explicit {{limit}}/{{offset}} markers with bound values and validates configured offset limits.

func ValidateOffset

func ValidateOffset(p Offset, options ...OffsetConfig) error

ValidateOffset enforces bounds without computing an overflowing offset.

Types

type Codec

type Codec struct {
	Provider  KeyProvider
	ActiveKey string
	Keys      map[string][]byte
	TTL       time.Duration
	MaxBytes  int
	Now       func() time.Time
}

Codec keys must be at least 32 random bytes. Keep previous keys available during rotation.

func (Codec) DecodeCursor

func (c Codec) DecodeCursor(token, query, filter string) (Cursor, error)

DecodeCursor verifies size, signature, expiration and query/filter binding before returning keys.

func (Codec) DecodeCursorContext

func (c Codec) DecodeCursorContext(ctx context.Context, token, query, filter string) (Cursor, error)

DecodeCursorContext verifies keys obtained from a cancelable provider.

func (Codec) EncodeCursor

func (c Codec) EncodeCursor(v Cursor) (string, error)

EncodeCursor authenticates cursor contents with the configured active HMAC key.

func (Codec) EncodeCursorContext

func (c Codec) EncodeCursorContext(ctx context.Context, v Cursor) (string, error)

EncodeCursorContext supports cancelable external key providers.

func (Codec) ValidateCursor

func (c Codec) ValidateCursor(token, query, filter string) error

ValidateCursor verifies a cursor without returning its decoded payload.

type Cursor

type Cursor struct {
	Version   int               `json:"v"`
	KeyID     string            `json:"kid"`
	Direction string            `json:"direction"`
	Keys      []json.RawMessage `json:"keys"`
	Query     string            `json:"query"`
	Filter    string            `json:"filter"`
	Expires   int64             `json:"exp,omitempty"`
}

Cursor contains versioned, query-bound composite pagination keys.

type CursorMeta

type CursorMeta struct {
	Type        string `json:"type"`
	Limit       int    `json:"limit"`
	Total       *int64 `json:"total_records,omitempty"`
	Next        string `json:"next_cursor,omitempty"`
	Previous    string `json:"previous_cursor,omitempty"`
	HasNext     bool   `json:"has_next"`
	HasPrevious bool   `json:"has_previous"`
}

CursorMeta describes a limit+1 query. Previous navigation requires reversing all sort directions then reversing the returned rows.

func NewCursorMeta

func NewCursorMeta(next, previous string) CursorMeta

NewCursorMeta constructs navigation metadata from issued cursors.

type CursorRequest

type CursorRequest struct {
	Cursor string
	Limit  int
}

CursorRequest contains bounded query parameters. Ordering direction is authenticated inside the cursor.

func ParseCursor

func ParseCursor(r *http.Request) (CursorRequest, error)

ParseCursor parses bounded cursor and limit request parameters.

type CursorResult

type CursorResult[T any] struct {
	Data []T        `json:"data"`
	Meta CursorMeta `json:"meta"`
}

CursorResult contains final presentation order and signed navigation cursors.

func FetchCursor

func FetchCursor[T any](ctx context.Context, selectRows SelectFunc, q Query, request CursorRequest, codec Codec, keys func(T) ([]any, error), count func(context.Context) (int64, error)) (CursorResult[T], error)

FetchCursor executes limit+1 rows and implements genuine reverse navigation by reversing SQL sort and presentation order. Keys must return values in Sort order. Count is optional and must count the same server-bound filter set.

type KeyProvider

type KeyProvider interface {
	Active(context.Context) (string, []byte, error)
	Key(context.Context, string) ([]byte, error)
}

KeyProvider is the consumer boundary for in-memory key rings, KMS-backed stores or other application-owned key sources.

type KeyRing

type KeyRing struct {
	// contains filtered or unexported fields
}

KeyRing supports atomic rotation. Keys are copied on write/read so callers cannot mutate concurrent signing material.

func NewKeyRing

func NewKeyRing(active string, keys map[string][]byte) (*KeyRing, error)

NewKeyRing validates the active key and retains previous verification keys for rotating cursors.

func (*KeyRing) Active

func (r *KeyRing) Active(ctx context.Context) (string, []byte, error)

Active obtains the current signing identity and a private key copy.

func (*KeyRing) Key

func (r *KeyRing) Key(ctx context.Context, id string) ([]byte, error)

Key looks up an allowed verification key; unknown key IDs never trigger network discovery.

func (*KeyRing) Rotate

func (r *KeyRing) Rotate(active string, keys map[string][]byte) error

Rotate replaces the entire ring atomically. Retain old keys until their issued cursors expire.

type KeyType

type KeyType string

KeyType fixes each decoded cursor parameter's type; no float64 intermediary is used for integer IDs.

const (
	KeyString  KeyType = "string"
	KeyInt64   KeyType = "int64"
	KeyFloat64 KeyType = "float64"
	KeyTime    KeyType = "time"
	KeyUUID    KeyType = "uuid"
	KeyBool    KeyType = "bool"
	KeyBytes   KeyType = "bytes"
)

type Offset

type Offset struct {
	Page  int `json:"page"`
	Limit int `json:"page_size"`
}

Offset contains a validated page number and bounded page size.

func ParseOffset

func ParseOffset(r *http.Request, options ...OffsetConfig) (Offset, error)

ParseOffset parses and validates page/limit query parameters.

func (Offset) Skip

func (p Offset) Skip() int

Skip returns the SQL offset; call Validate before use.

func (Offset) Validate

func (p Offset) Validate() error

Validate checks the supplied value according to package validation rules.

type OffsetConfig

type OffsetConfig struct{ DefaultLimit, MaxLimit, MaxOffset int }

OffsetConfig can reduce the default/max page size and bound database offsets. MaxOffset=0 disables the additional offset cap.

type OffsetMeta

type OffsetMeta struct {
	Type string `json:"type"`
	Offset
	Total       int64 `json:"total_records"`
	Pages       int64 `json:"total_pages"`
	HasNext     bool  `json:"has_next"`
	HasPrevious bool  `json:"has_previous"`
}

OffsetMeta contains offset pagination totals and navigation flags.

func NewOffsetMeta

func NewOffsetMeta(p Offset, total int64) (OffsetMeta, error)

NewOffsetMeta calculates page counts and navigation flags without overflowing totals.

type Query

type Query struct {
	Dialect, Template, Identity, Filter string
	Args                                []any
	Columns                             map[string]string
	Sort                                []Sort
	KeyTypes                            []KeyType
}

Query describes a developer-owned SQL template containing exactly one {{where}}, {{order}} and {{limit}} marker. Args bind base filters, whose placeholders must precede generated placeholders. Never put user input in Template or Columns.

type SQLPage

type SQLPage struct {
	Where, OrderBy string
	Args           []any
	Reverse        bool
}

SQLPage contains bound seek SQL and its required result ordering.

func Keyset

func Keyset(dialect string, allow map[string]string, sorts []Sort, values []any, previous bool, start int) (SQLPage, error)

Keyset builds a lexicographic seek predicate. Values are in Sort order; nil represents SQL NULL. PostgreSQL placeholders start at start+1. MySQL uses ?. Previous pages must reverse result rows.

type SelectFunc

type SelectFunc func(context.Context, any, string, ...any) error

SelectFunc matches both SQL adapters without importing either driver. It must fill a slice pointer and close its streams.

type Sort

type Sort struct {
	Name                           string
	Descending, NullsFirst, Unique bool
}

Sort names refer to an explicit allowlist. The final column must be unique for deterministic paging.

Jump to

Keyboard shortcuts

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