# page

```go
import "apistock.dev/page"
```

Package page provides cursor pagination and sorting for list endpoints: bounded limits, opaque cursors and allowlisted sort fields.

Embed [Params](#Params) in a Huma input struct, or call [Parse](#Parse) with URL query values, then convert to a validated [Request](#Request) against the endpoint's [Options](#Options).

Cursors are opaque but not signed. They must only encode positions (for example the last item's sort key and ID); repositories still apply authorisation and tenant filters to every query.

Stability: pre-1.0 (ADR-0015).

## Constants

### const DefaultLimit, …

```go
const (
	DefaultLimit = 20
	MaxLimit     = 100
	// MaxCursorLength bounds cursor size to limit abuse.
	MaxCursorLength = 512
)
```

Limits used when [Options](#Options) leaves them zero.

## Variables

### var ErrInvalidLimit, …

```go
var (
	ErrInvalidLimit  = errors.New("page: invalid limit")
	ErrInvalidCursor = errors.New("page: invalid cursor")
	ErrInvalidSort   = errors.New("page: invalid sort")
)
```

Errors returned for invalid pagination input.

## Functions

### func DecodeCursor

```go
func DecodeCursor(cursor string, position any) error
```

DecodeCursor decodes a cursor produced by [EncodeCursor](#EncodeCursor) into position.

### func EncodeCursor

```go
func EncodeCursor(position any) (string, error)
```

EncodeCursor returns an opaque cursor for position, which must be JSON-encodable.

## Types

### type Options

```go
type Options struct {
	DefaultLimit int // zero means DefaultLimit
	MaxLimit     int // zero means MaxLimit
	// SortFields is the allowlist of sortable fields. Empty means sorting is not allowed.
	SortFields []string
	// DefaultSort applies when the request has no sort.
	DefaultSort []SortField
}
```

Options configure what a list endpoint accepts.

### type Params

```go
type Params struct {
	Limit  int    `query:"limit" minimum:"0" maximum:"100" doc:"Maximum number of items to return (default 20, max 100)"`
	Cursor string `query:"cursor" maxLength:"512" doc:"Opaque cursor from a previous response's next_cursor"`
	Sort   string `query:"sort" maxLength:"200" doc:"Comma-separated sort fields; prefix a field with - for descending order" example:"-created_at"`
}
```

Params are the raw query parameters of a list endpoint. The struct tags make Huma document and validate them.

#### func (Params) Request

```go
func (p Params) Request(opts Options) (Request, error)
```

Request validates p against opts.

### type Request

```go
type Request struct {
	Limit  int
	Cursor string
	Sort   []SortField
}
```

Request is a validated pagination request.

#### func Parse

```go
func Parse(q url.Values, opts Options) (Request, error)
```

Parse reads limit, cursor and sort from URL query values.

### type Result

```go
type Result[T any] struct {
	Items      []T    `json:"items"`
	NextCursor string `json:"next_cursor,omitempty" doc:"Pass as cursor to get the next page; absent on the last page"`
}
```

Result is a page of items. NextCursor is empty on the last page.

### type SortField

```go
type SortField struct {
	Field string
	Desc  bool
}
```

SortField is one validated sort key.

