URLs


Pagy uses the current URL as the base to generate all the other page URLs. It retrieves it from the self.request when available, or from a :request option set to a Rack::Request or a simple Hash of :base_url, :path, :params (and a pagy :cookie value in case of Keynav pagination).

These options give you full control over the URL composition for paginator and helper:

jsonapi: true
Enables JSON:API-compliant URLs with nested query string (e.g., ?page[number]=2&page[size]=100).
root_key: 'my_root'
Set it to enable nested URLs with nested query string ?my_root[page]=2&my_root[limit]=100)). Use it to handle multiple pagination objects in the same request.
page_key: 'my_page'
Set it to change the key string used for the :page in URLs (default 'page').
limit_key: 'my_limit'
Set it to change the key string used for the :limit in URLs (default 'limit').
absolute: true
Makes the URL absolute.
path: '/my_path'
Overrides the request path in pagination URLs. Use the path only (not the absolute URL). (see Override the request path)
fragment: '...'
URL fragment string.
querify: tweak

Set it to a Lambda to directly edit the passed string-keyed params hash itself. Its result is ignored.

tweak = ->(q) { q.except!('not_useful').merge!('custom' => 'useful') }

Serving paginated collections is a retrieval action that does not modify data; therefore, it should rely on GET requests. Since most applications follow this standard, Pagy parses and produces GET URLs out of the box.

If you must use POST to retrieve paginated collections, you should build your own POST forms/requests using the data provided by Pagy (e.g., via the data_hash or instance readers). However, Pagy parses and handles POST request parameters out of the box without additional configuration.

Routers (like the Rails' one) allow defining parameters as part of the path (e.g., /items/:page)...

Why?

The Cons are overwhelming.

Pros
  • Aesthetically cleaner URLs
  • Possibility to cache single pages at the edge (rarely necessary)
Cons
  • RFC 3986 Compliance
    • The :page parameter represents non-hierarchical data, which fits the definition of the query string component.
    • It does not fit the definition of the path component, which represents hierarchical resources.
  • Data Identification
    • A query string parameter is labeled data (?page=2) identifiable without external context.
    • A path segment (/2) is unlabeled and relies on external routing logic to be handled, so it cannot be identified/used by agnostic code.
  • Performance
    • Dynamic segments are framework-specific routing concepts, not query params concepts.
    • Using framework code is not only non-agnostic, but significantly slower than pagy's generic query param handling.
    • Using it (or even just checking for it) would be an unnecessary burden for all the apps.

enable_rails_page_segment.rb
# frozen_string_literal: true

# ################# IMPORTANT WARNING #################
# This setup forces Pagy to use the Rails `url_for` method,
# which is significantly slower (~20x) than Pagy's native URL generation.

# Use this file ONLY if you absolutely want to support the page param as a dynamic segment.
# (e.g. get '/comments(/:page)', to: 'comments#index').
# #####################################################

# USAGE

# initializers/pagy.rb
# require Pagy::ROOT.join('apps/enable_rails_page_segment.rb')

# config/routes.rb (example)
# get '/comments(/:page)', to: 'comments#index'


# Use plain Strings tokens instead of Pagy::EscapedValue strings
Pagy.send(:remove_const, :PAGE_TOKEN)  ; Pagy::PAGE_TOKEN  = '___PAGY_PAGE___'
Pagy.send(:remove_const, :LIMIT_TOKEN) ; Pagy::LIMIT_TOKEN = '___PAGY_LIMIT___'

# Require the pagy sources to override
require_relative '../lib/pagy/toolbox/paginators/method'
require_relative '../lib/pagy/modules/abilities/linkable'

class Pagy
  # Switch to the `request.params` to get access to rails-added path parameters
  module RequestOverride
    def get_params(request)
      request.params
    end
  end
  Request.prepend RequestOverride

  # Inject the caller context into the Pagy instance
  module MethodOverride
    def pagy(...)
      super.tap { _1[0].instance_variable_set(:@context, self) }
    end
  end
  Method.prepend MethodOverride

  # Compose the final URL using `url_for`.
  module LinkableOverride
    def compose_url(absolute, _path, params, fragment)
      params[:anchor]    = fragment if fragment
      params[:only_path] = !absolute
      @context.url_for(params)
    end
  end
  Linkable.prepend LinkableOverride
end