Skip to main content
Version: Next

Paginate

Pagination middleware for Fiber that extracts pagination parameters from query strings and stores them in the request context. Supports page-based, offset-based, and cursor-based pagination with multi-field sorting.

Signatures

func New(config ...Config) fiber.Handler
func FromContext(ctx any) (*PageInfo, bool)

FromContext accepts fiber.CustomCtx, fiber.Ctx, *fasthttp.RequestCtx, or context.Context.

Examples

Import the middleware package:

import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/paginate"
)

Once your Fiber app is initialized, choose one of the following approaches:

Basic Usage

app.Use(paginate.New())

app.Get("/users", func(c fiber.Ctx) error {
pageInfo, ok := paginate.FromContext(c)
if !ok {
return fiber.ErrBadRequest
}

// Use pageInfo.Page, pageInfo.Limit, pageInfo.Start()
// GET /users?page=2&limit=20 → Page: 2, Limit: 20, Start(): 20
return c.JSON(pageInfo)
})

Sorting

app.Use(paginate.New(paginate.Config{
SortKey: "sort",
DefaultSort: "id",
AllowedSorts: []string{"id", "name", "created_at"},
}))

// GET /users?sort=name,-created_at
// → Sort: [{Field: "name", Order: "asc"}, {Field: "created_at", Order: "desc"}]

Cursor Pagination

app.Use(paginate.New())

app.Get("/feed", func(c fiber.Ctx) error {
pageInfo, ok := paginate.FromContext(c)
if !ok {
return fiber.ErrBadRequest
}

if pageInfo.Cursor != "" {
// Decode the cursor to get keyset values
values := pageInfo.CursorValues()
// Use values["id"], values["created_at"], etc. for WHERE clause
}

// results is a slice of items from your database query
// After fetching results, set the next cursor for the client
if len(results) > 0 {
lastItem := results[len(results)-1]
if err := pageInfo.SetNextCursor(map[string]any{
"id": lastItem.ID,
"created_at": lastItem.CreatedAt,
}); err != nil {
return err
}
}

return c.JSON(fiber.Map{
"data": results,
"has_more": pageInfo.HasMore,
"next_cursor": pageInfo.NextCursor,
})
})

// First request: GET /feed?limit=20
// Next request: GET /feed?cursor=<token>&limit=20

Custom Configuration

app.Use(paginate.New(paginate.Config{
PageKey: "p",
LimitKey: "size",
DefaultPage: 1,
DefaultLimit: 25,
SortKey: "order_by",
DefaultSort: "created_at",
AllowedSorts: []string{"created_at", "name", "email"},
CursorKey: "after",
CursorParam: "starting_after",
}))

Config

PropertyTypeDescriptionDefault
Nextfunc(fiber.Ctx) boolNext defines a function to skip this middleware when returned true.nil
PageKeystringQuery string key for page number."page"
DefaultPageintDefault page number.1
LimitKeystringQuery string key for limit."limit"
DefaultLimitintDefault items per page.10
SortKeystringQuery string key for sort.""
DefaultSortstringDefault sort field."id"
AllowedSorts[]stringWhitelist of allowed sort fields. If nil, all fields are allowed.nil
OffsetKeystringQuery string key for offset."offset"
CursorKeystringQuery string key for cursor-based pagination."cursor"
CursorParamstringOptional alias for the cursor query key.""
MaxLimitintMaximum items per page.100

Default Config

var ConfigDefault = Config{
Next: nil,
PageKey: "page",
DefaultPage: 1,
LimitKey: "limit",
DefaultLimit: 10,
MaxLimit: 100,
DefaultSort: "id",
OffsetKey: "offset",
CursorKey: "cursor",
}

PageInfo

The PageInfo struct is stored in the request context and provides:

MethodDescription
Start() intReturns calculated start index (from page/limit or offset)
SortBy(field, order)Adds a sort field (chainable)
NextPageURL(baseURL)Generates next page URL with default keys
NextPageURLWithKeys(baseURL, pageKey, limitKey)Generates next page URL with custom query keys
PreviousPageURL(baseURL)Generates previous page URL (empty on page 1)
PreviousPageURLWithKeys(baseURL, pageKey, limitKey)Generates previous page URL with custom query keys
NextCursorURL(baseURL)Generates next cursor URL (empty if no more)
NextCursorURLWithKeys(baseURL, cursorKey, limitKey)Generates next cursor URL with custom query keys
CursorValues()Decodes cursor token into key-value map
SetNextCursor(values)Encodes values into cursor token, sets HasMore; returns error

Safety

  • Limit is capped at MaxLimit (default: 100, configurable) to prevent excessive memory usage
  • Page values below 1 reset to 1
  • Negative offsets reset to 0
  • Sort fields are validated against AllowedSorts
  • Cursor tokens exceeding 2048 characters are rejected with 400 Bad Request
  • SetNextCursor returns an error if the encoded token would exceed 2048 characters, preventing the server from issuing cursors it would later reject
  • Invalid cursor tokens return 400 Bad Request via Fiber's error handler
  • If DefaultSort is not included in AllowedSorts, it falls back to the first allowed sort field
  • URL helpers preserve existing query parameters when building pagination links