paging

package
v1.149.0 Latest Latest
Warning

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

Go to latest
Published: Jul 13, 2026 License: MIT Imports: 1 Imported by: 0

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

func ComputeOffsetAndLimit(currentPage, pageSize uint) (uint, uint)

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

func New(currentPage, pageSize, totalItems uint) Paging

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}

Jump to

Keyboard shortcuts

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