Skip to content

Pagination

ss-keel-core provides built-in helpers for offset-based pagination. ParsePagination reads query parameters from the request and Page[T] is the standard paginated response envelope.

func (c *Ctx) ParsePagination() PageQuery

Reads ?page and ?limit from the query string and returns a PageQuery.

ParameterDefaultMax
page1
limit20100
func (c *UserController) list(ctx *core.Ctx) error {
q := ctx.ParsePagination()
// q.Page = 1 (or ?page=N)
// q.Limit = 20 (or ?limit=N, max 100)
users, total, err := c.service.List(ctx.Context(), q)
if err != nil {
return err
}
return ctx.OK(core.NewPage(users, total, q.Page, q.Limit))
}
type PageQuery struct {
Page int
Limit int
}

Pass PageQuery to your repository or service to perform the actual query:

// In your repository
func (r *UserRepo) FindAll(ctx context.Context, q core.PageQuery) (core.Page[User], error) {
offset := (q.Page - 1) * q.Limit
rows, err := r.db.QueryContext(ctx,
"SELECT * FROM users LIMIT $1 OFFSET $2",
q.Limit, offset,
)
// ...
total := countUsers(ctx)
return core.NewPage(users, total, q.Page, q.Limit), nil
}
type Page[T any] struct {
Data []T `json:"data"`
Total int `json:"total"`
Page int `json:"page"`
Limit int `json:"limit"`
TotalPages int `json:"total_pages"`
}
func NewPage[T any](data []T, total, page, limit int) Page[T]

TotalPages is calculated automatically: ceil(total / limit).

page := core.NewPage(users, 142, 2, 20)
// {
// "data": [...],
// "total": 142,
// "page": 2,
// "limit": 20,
// "total_pages": 8
// }
GET /users?page=2&limit=10
{
"data": [
{ "id": "1", "name": "Alice" },
{ "id": "2", "name": "Bob" }
],
"total": 42,
"page": 2,
"limit": 10,
"total_pages": 5
}

The Repository[T, ID] interface uses PageQuery and Page[T] by convention:

type Repository[T any, ID any] interface {
FindAll(ctx context.Context, q PageQuery) (Page[T], error)
// ...
}

See Interfaces — Repository for the full interface.