Documentation
¶
Overview ¶
Package paging computes all pagination metadata — current page, total pages, previous/next page numbers, and SQL OFFSET/LIMIT values — from three inputs: current page number, page size, and total item count.
Problem ¶
Paginating API responses and database queries requires the same arithmetic repeatedly: clamping out-of-range page numbers, computing total pages with correct ceiling division, deriving OFFSET for SQL, and determining safe previous/next page links that never go below 1 or above the last page. Getting any one of these details wrong produces broken navigation links, off-by-one errors in SQL queries, or panics on empty result sets.
Solution ¶
New accepts the three caller-supplied values and returns a fully populated Paging struct in a single call. Out-of-range inputs are clamped automatically so callers never need to guard against zero page sizes or page numbers beyond the last page:
p := paging.New(currentPage, pageSize, totalItems) // p.Offset and p.PageSize are ready for use in a SQL LIMIT/OFFSET clause. // p.PreviousPage and p.NextPage are safe to embed in a JSON response.
For cases where only SQL values are needed:
offset, limit := paging.ComputeOffsetAndLimit(currentPage, pageSize)
Features ¶
- Single-call API: New computes every pagination field at once — no manual arithmetic required in application code.
- Safe input clamping: page numbers below 1 are raised to 1; page numbers beyond the last page are clamped to Paging.TotalPages; page sizes below 1 are raised to 1. Callers can pass raw query-string values directly.
- Correct ceiling division: total-page calculation uses integer ceiling division so the last partial page is never lost.
- Boundary-safe navigation: Paging.PreviousPage is always >= 1 and Paging.NextPage is always <= Paging.TotalPages, making them safe to embed in API responses and hypermedia links without further validation.
- Explicit navigation flags: Paging.HasPreviousPage and Paging.HasNextPage report whether an adjacent page actually exists, so callers can show or hide navigation links without re-deriving boundary conditions.
- SQL-ready offset: Paging.Offset is the zero-based row offset for use directly in a SQL OFFSET clause.
- JSON-serialisable: all Paging fields carry json struct tags, so the struct can be returned as part of an API envelope without a separate DTO.
- Lightweight SQL helper: ComputeOffsetAndLimit provides the two SQL values without constructing a full Paging struct.
- Zero dependencies: uses only the Go standard library.
Example ¶
For 17 items displayed 5 per page, navigating to page 3:
p := paging.New(3, 5, 17) // p.CurrentPage == 3 // p.PageSize == 5 // p.TotalItems == 17 // p.TotalPages == 4 // p.PreviousPage == 2 // p.NextPage == 4 // p.Offset == 10 (used as SQL OFFSET) // p.HasPreviousPage == true // p.HasNextPage == true
Edge cases ¶
- Empty result set: with totalItems == 0, New returns TotalPages == 1, CurrentPage == 1 and Offset == 0 (never zero pages and never a panic). Callers detect an empty set via TotalItems == 0, not via the page fields.
- Offset overflow: if the offset multiplication overflows uint — only reachable through ComputeOffsetAndLimit with a currentPage far beyond the data, since New clamps currentPage to the last page — the offset is clamped to math.MaxUint as a "beyond range" sentinel that selects no rows, rather than wrapping to a wrong offset.
- JSON numeric precision: fields are plain integers, but JSON numbers are float64 in some clients (e.g. JavaScript), which cannot represent values above 2^53 exactly. Realistic pagination magnitudes stay well below that; the math.MaxUint offset sentinel does not, so treat it as "no rows".
Benefits ¶
This package eliminates repetitive, error-prone pagination arithmetic from application and data-access code, providing safe, consistent pagination metadata in a single import.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ComputeOffsetAndLimit ¶
ComputeOffsetAndLimit returns the zero-based SQL OFFSET and LIMIT (page size) for the given currentPage and pageSize, auto-clamping both to minimum values of 1. Unlike New, it has no totalItems to bound against, so it does not clamp currentPage to a last page: a page far beyond the data yields a correspondingly large offset, clamped to math.MaxUint on multiplication overflow so a wrapped offset can never select the wrong rows.
Example ¶
package main
import (
"fmt"
"github.com/tecnickcom/nurago/pkg/paging"
)
func main() {
var (
currentPage uint = 3
pageSize uint = 5
)
offset, limit := paging.ComputeOffsetAndLimit(currentPage, pageSize)
fmt.Println(offset)
fmt.Println(limit)
}
Output: 10 5
Types ¶
type Paging ¶
type Paging struct {
// CurrentPage is the current page number starting from 1.
// It is encoded as "page" to mirror the conventional ?page= query parameter.
CurrentPage uint `json:"page"`
// PageSize is the maximum number of items that can be contained in a page. It is also the LIMIT in SQL queries.
PageSize uint `json:"page_size"`
// TotalItems is the total number of items to be paginated.
TotalItems uint `json:"total_items"`
// TotalPages is the total number of pages required to contain all the items. It is always >= 1, even for an empty result set.
TotalPages uint `json:"total_pages"`
// PreviousPage is the previous page. It is equal to 1 if we are on the first page (CurrentPage == 1).
PreviousPage uint `json:"previous_page"`
// NextPage is the next page. It is equal to TotalPages if we are on the last page (CurrentPage == TotalPages).
NextPage uint `json:"next_page"`
// Offset is the zero-based number of items before the current page. It is the OFFSET in SQL queries.
Offset uint `json:"offset"`
// HasPreviousPage reports whether a page before CurrentPage exists (CurrentPage > 1).
HasPreviousPage bool `json:"has_previous_page"`
// HasNextPage reports whether a page after CurrentPage exists (CurrentPage < TotalPages).
HasNextPage bool `json:"has_next_page"`
}
Paging contains all pagination metadata computed from current page, page size, and total item count; all fields are JSON-serializable for API responses.
func New ¶
New computes all pagination metadata, clamping inputs to safe ranges: pageSize and currentPage default to 1 if less, and currentPage is clamped to totalPages. An empty result set (totalItems == 0) yields a single empty page.
Example ¶
package main
import (
"fmt"
"github.com/tecnickcom/nurago/pkg/paging"
)
func main() {
var (
currentPage uint = 3
pageSize uint = 5
totalItems uint = 17
)
// calculate new paging parameters
p := paging.New(currentPage, pageSize, totalItems)
fmt.Println(p)
}
Output: {3 5 17 4 2 4 10 true true}