Pagex ๐Ÿ“–

github

Hex.pmDocumentationLicense: MIT

Fast, minimal, production-ready pagination for Ecto and Phoenix.

Inspired by Pagy (Ruby), Pagex is designed specifically for the Elixir/Phoenix ecosystem. It takes the approach of explicit, small functions over magic. No use Pagex in your schema, no global config macros, and no hidden schema introspection.


๐Ÿš€ Example Integration

If you want to see a full, production-ready implementation of pagex in a Phoenix application, check out our example repository:

๐Ÿ‘‰ Simple Pagination Example

This example project demonstrates:

๐Ÿ“บ Demo Videos

Phoenix LiveView Integration

See how smoothly pagex handles real-time pagination in LiveView:

Click to view LiveView Demo

JSON API Response

Standardized metadata structure ready for any front-end:

Click to view JSON API Demo

โœจ Features & Comparison

Pagex offers a modern alternative to existing pagination libraries by combining performance with extreme simplicity.

Feature Pagex Scrivener Paginator
Offset pagination โœ… โœ… โœ…
Cursor pagination โœ… โŒ โœ…
LiveView helpers โœ… โŒ โŒ
JSON API support โœ… Partial Partial
No macros/DSLs โœ… โŒ โŒ
Max page size guard โœ… โŒ โŒ
Optional count query โœ… โŒ โŒ

Pagination Benchmark

Comparing offset-based vs cursor-based pagination โ€” with and without COUNT(*) โ€” across different page depths.


Test Environment

Parameter Value
Page size (not specified)
Dataset size (not specified)
Database (add here)
Measurement Average latency, p99 latency, memory usage per query
Cache (add warm/cold if relevant)

Key Findings


Performance Results

Method Throughput (ops/sec) Avg Latency Deviation Median p99
Offset page 1 (no count) 16.49K 60.64 ยตs ยฑ12.79% 59.67 ยตs 81.04 ยตs
Cursor first page 15.44K 64.77 ยตs ยฑ27.23% 60.29 ยตs 146.58 ยตs
Offset page 100 (no count) 12.18K 82.07 ยตs ยฑ8.56% 80.96 ยตs 100.05 ยตs
Cursor next page chain 8.22K 121.71 ยตs ยฑ9.15% 119.92 ยตs 160.47 ยตs
Offset page 1 (with count) 0.37K 2731.43 ยตs ยฑ5.89% 2738.56 ยตs 3195.07 ยตs
Offset page 100 (with count) 0.36K 2759.19 ยตs ยฑ5.48% 2766.46 ยตs 3148.26 ยตs

Relative Speed (vs. fastest baseline)

Method Relative Speed Latency Delta
Offset page 1 (no count) 1.00ร— (baseline) โ€”
Cursor first page 1.07ร— slower +4.13 ยตs
Offset page 100 (no count) 1.35ร— slower +21.44 ยตs
Cursor next page chain 2.01ร— slower +61.07 ยตs
Offset page 1 (with count) 45.05ร— slower +2670.79 ยตs
Offset page 100 (with count) 45.50ร— slower +2698.55 ยตs

Memory Usage

Method Avg Memory Deviation Median p99
Offset page 1 (no count) 55.06 KB ยฑ0.07% 55.05 KB 55.19 KB
Cursor first page 53.68 KB ยฑ0.02% 53.67 KB 53.72 KB
Offset page 100 (no count) 55.30 KB ยฑ0.02% 55.30 KB 55.34 KB
Cursor next page chain 109.01 KB ยฑ0.01% 109.02 KB 109.02 KB
Offset page 1 (with count) 76.89 KB ยฑ0.27% 76.83 KB 77.75 KB
Offset page 100 (with count) 77.10 KB ยฑ0.16% 77.09 KB 77.59 KB

Relative Memory (vs. baseline)

Method Relative Memory Delta
Offset page 1 (no count) 1.00ร— (baseline) โ€”
Cursor first page 0.97ร— โˆ’1.38 KB
Offset page 100 (no count) 1.00ร— +0.24 KB
Cursor next page chain 1.98ร— +53.95 KB
Offset page 1 (with count) 1.40ร— +21.83 KB
Offset page 100 (with count) 1.40ร— +22.04 KB

Conclusion

Offset pagination without COUNT(*) performs best for shallow pages and simple use cases, but degrades with expensive counting operations.

Cursor-based pagination provides more predictable scaling for deeper pagination, but introduces higher per-request latency and increased memory usage when chaining queries.

When to use what

Scenario Recommendation
Simple, shallow listing pages โœ… Offset pagination (no count)
Deep pagination / large datasets โœ… Cursor pagination
High-traffic hot paths โ›” Avoid COUNT(*)

๐Ÿ“ฆ Installation

Add pagex to your list of dependencies in mix.exs:

# mix.exs
def deps do
  [
    {:pagex, "~> 0.2.3"}
  ]
end

Then, run mix deps.get in your terminal.


๐Ÿ“– Documentation

Full API documentation is available via HexDocs:

๐Ÿ‘‰ https://hexdocs.pm/pagex

To generate and view the full API documentation locally:

mix docs

Then open:

open doc/index.html

On Linux:

xdg-open doc/index.html

Or simply open doc/index.html in your browser.


๐Ÿš€ Quick Start

Pagex provides a clean and straightforward API. You can paginate your Ecto queries using either Offset or Cursor-based pagination.

Offset Pagination

Ideal for standard table views and web interfaces.

alias App.Repo
alias App.Blog.Post

# Using default params
{posts, meta} = Pagex.paginate(Post, params, Repo)

# With a custom Ecto query
query = from p in Post, where: p.published == true, order_by: [desc: p.inserted_at]
{posts, meta} = Pagex.paginate(query, %{"page" => 2, "page_size" => 20}, Repo)

Cursor Pagination

Highly recommended for large datasets, JSON APIs, and infinite scrolling interfaces.

{posts, meta} = Pagex.paginate_cursor(Post, params, Repo)
(Note: The returned meta struct contains helpful data like next_cursor, prev_cursor, total_pages, etc., depending on your chosen pagination strategy.)

๐Ÿ’ป Phoenix & LiveView Integration

Pagex is built with Phoenix in mind. While staying un-intrusive, it ships with out-of-the-box helpers for:


Project Structure

pagex/
โ”œโ”€โ”€ lib/
โ”‚   โ”œโ”€โ”€ pagex.ex                  # Public API โ€” paginate/4 and paginate_cursor/4
โ”‚   โ””โ”€โ”€ pagex/
โ”‚       โ”œโ”€โ”€ meta.ex               # Meta struct + constructors
โ”‚       โ”œโ”€โ”€ params.ex             # Parameter validation
โ”‚       โ”œโ”€โ”€ offset.ex             # Offset pagination engine
โ”‚       โ”œโ”€โ”€ cursor.ex             # Cursor pagination engine
โ”‚       โ”œโ”€โ”€ phoenix/
โ”‚             โ””โ”€โ”€ live_view.ex    # Phoenix LiveView helpers
โ”‚             โ””โ”€โ”€ html.ex       # HTML helper functions
โ”‚ 
โ”‚ 
โ”‚               
โ”œโ”€โ”€ test/
โ”‚   โ””โ”€โ”€ pagex/
โ”‚       โ”œโ”€โ”€ params_test.exs
โ”‚       โ”œโ”€โ”€ meta_test.exs
โ”‚       โ”œโ”€โ”€ cursor_encode_test.exs
โ”‚       โ””โ”€โ”€ phoenix/
โ”‚           โ”œโ”€โ”€ html_test.exs
โ”‚           โ”œโ”€โ”€ live_view_test.exs
โ”œโ”€โ”€ benchmarks/
โ”‚   โ””โ”€โ”€ support/
โ”‚       โ”œโ”€โ”€ post.ex
โ”‚       โ”œโ”€โ”€ repo.ex
โ”‚   โ””โ”€โ”€ pagination_benchmark.exs
โ”‚   โ””โ”€โ”€ setup.exs
โ”œโ”€โ”€ mix.exs
โ”œโ”€โ”€ README.md
โ”œโ”€โ”€ CHANGELOG.md
โ””โ”€โ”€ LICENSE

๐Ÿค Contributing

Contributions, bug reports, and feature requests are welcome!

Feel free to check the issues page to get involved.

  1. Fork and Clone

First, fork the repository by clicking the "Fork" button at the top right of this page [1]. Then, clone your fork to your local machine:

    git clone https://github.com/Null-logic-0/pagex.git
    cd pagex
  1. Install Dependencies

Pagex is an Elixir project. Fetch the required dependencies using mix:

    mix deps.get
  1. Create a Branch

Create a new branch for your feature, improvement, or bug fix:

    git checkout -b feature/my-awesome-feature
  1. Make Your Changes

Write your code and implement your changes. If you are adding a new feature or fixing a bug, please write tests to cover your changes to maintain the library's stability.

  1. Run Tests & Format Code

Before committing, ensure that all tests pass and that the code adheres to the standard Elixir formatting rules:

# Run the test suite
mix test

# Format the code
mix format
  1. Commit and Push

Commit your changes with a descriptive message and push the branch to your fork:

git add .
git commit -m "Add my awesome feature"
git push origin feature/my-awesome-feature
  1. Open a Pull Request

Go back to the main Pagex repository and you'll see a prompt to open a Pull Request. Submit your PR against the master branch and describe the changes you've made!

Why this is helpful:


๐Ÿ“œ License

Pagex is open-source software released under the MIT LICENSE