{"id":13879846,"url":"https://github.com/davidcelis/api-pagination","last_synced_at":"2026-04-02T22:04:55.612Z","repository":{"id":9241151,"uuid":"11061183","full_name":"davidcelis/api-pagination","owner":"davidcelis","description":":page_facing_up: Link header pagination for Rails and Grape APIs.","archived":false,"fork":false,"pushed_at":"2024-07-29T16:27:16.000Z","size":195,"stargazers_count":687,"open_issues_count":24,"forks_count":137,"subscribers_count":15,"default_branch":"master","last_synced_at":"2025-04-13T18:44:35.865Z","etag":null,"topics":["api","grape","kaminari","pagination","rails","ruby","willpaginate"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"tymm/zsh-directory-history","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/davidcelis.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2013-06-29T23:09:55.000Z","updated_at":"2025-02-14T15:51:23.000Z","dependencies_parsed_at":"2025-04-13T18:34:24.140Z","dependency_job_id":"84753255-c62d-41b7-bd58-1f3003dc7d60","html_url":"https://github.com/davidcelis/api-pagination","commit_stats":null,"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidcelis%2Fapi-pagination","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidcelis%2Fapi-pagination/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidcelis%2Fapi-pagination/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidcelis%2Fapi-pagination/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davidcelis","download_url":"https://codeload.github.com/davidcelis/api-pagination/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254235696,"owners_count":22036963,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","grape","kaminari","pagination","rails","ruby","willpaginate"],"created_at":"2024-08-06T08:02:35.633Z","updated_at":"2026-04-02T22:04:55.576Z","avatar_url":"https://github.com/davidcelis.png","language":"Ruby","readme":"# api-pagination\n\nPaginate in your headers, not in your response body.\nThis follows the proposed [RFC-8288](https://tools.ietf.org/html/rfc8288) standard for Web linking.\n\n## Installation\n\nIn your `Gemfile`:\n\n```ruby\n# Requires Rails (Rails-API is also supported), or Grape\n# v0.10.0 or later. If you're on an earlier version of\n# Grape, use api-pagination v3.0.2.\ngem 'rails', '\u003e= 3.0.0'\ngem 'rails-api'\ngem 'grape', '\u003e= 0.10.0'\n\n# Then choose your preferred paginator from the following:\ngem 'pagy'\ngem 'kaminari'\ngem 'will_paginate'\n\n# Finally...\ngem 'api-pagination'\n```\n\n## Configuration (optional)\n\nBy default, api-pagination will detect whether you're using Pagy, Kaminari, or WillPaginate, and it will name headers appropriately. If you want to change any of the configurable settings, you may do so:\n\n```ruby\nApiPagination.configure do |config|\n  # If you have more than one gem included, you can choose a paginator.\n  config.paginator = :kaminari # or :will_paginate\n\n  # By default, this is set to 'Total'\n  config.total_header = 'X-Total'\n\n  # By default, this is set to 'Per-Page'\n  config.per_page_header = 'X-Per-Page'\n\n  # Optional: set this to add a header with the current page number.\n  config.page_header = 'X-Page'\n\n  # Optional: set this to add other response format. Useful with tools that define :jsonapi format\n  config.response_formats = [:json, :xml, :jsonapi]\n\n  # Optional: what parameter should be used to set the page option\n  config.page_param = :page\n  # or\n  config.page_param do |params|\n    params[:page][:number] if params[:page].is_a?(ActionController::Parameters)\n  end\n\n  # Optional: what parameter should be used to set the per page option\n  config.per_page_param = :per_page\n  # or\n  config.per_page_param do |params|\n    params[:page][:size] if params[:page].is_a?(ActionController::Parameters)\n  end\n\n  # Optional: Include the total and last_page link header\n  # By default, this is set to true\n  # Note: When using kaminari, this prevents the count call to the database\n  config.include_total = false\nend\n```\n\n### Pagy-specific configuration\n\nPagy does not have a built-in way to specify a maximum number of items per page, but `api-pagination` will check if you've set a `:max_per_page` variable. To configure this, you can use the following code somewhere in an initializer:\n\n```ruby\nPagy::DEFAULT[:max_per_page] = 100\n```\n\nIf left unconfigured, clients can request as many items per page as they wish, so it's highly recommended that you configure this.\n\n## Rails\n\nIn your controller, provide a pageable collection to the `paginate` method. In its most convenient form, `paginate` simply mimics `render`:\n\n```ruby\nclass MoviesController \u003c ApplicationController\n  # GET /movies\n  def index\n    movies = Movie.all # Movie.scoped if using ActiveRecord 3.x\n\n    paginate json: movies\n  end\n\n  # GET /movies/:id/cast\n  def cast\n    actors = Movie.find(params[:id]).actors\n\n    # Override how many Actors get returned. If unspecified,\n    # params[:per_page] (which defaults to 25) will be used.\n    paginate json: actors, per_page: 10\n  end\nend\n```\n\nThis will pull your collection from the `json` or `xml` option, paginate it for you using `params[:page]` and `params[:per_page]`, render Link headers, and call `ActionController::Base#render` with whatever you passed to `paginate`. This should work well with [ActiveModel::Serializers](https://github.com/rails-api/active_model_serializers). However, if you need more control over what is done with your paginated collection, you can pass the collection directly to `paginate` to receive a paginated collection and have your headers set. Then, you can pass that paginated collection to a serializer or do whatever you want with it:\n\n```ruby\nclass MoviesController \u003c ApplicationController\n  # GET /movies\n  def index\n    movies = paginate Movie.all\n\n    render json: MoviesSerializer.new(movies)\n  end\n\n  # GET /movies/:id/cast\n  def cast\n    actors = paginate Movie.find(params[:id]).actors, per_page: 10\n\n    render json: ActorsSerializer.new(actors)\n  end\nend\n```\n\nNote that the collection sent to `paginate` _must_ respond to your paginator's methods. This is typically fine unless you're dealing with a stock Array. For Kaminari, `Kaminari.paginate_array` will be called for you behind-the-scenes. For WillPaginate, you're out of luck unless you call `require 'will_paginate/array'` somewhere. Because this pollutes `Array`, it won't be done for you automatically. If you use Pagy, it doesn't matter, because Pagy doesn't care what you're paginating. It will just work, as long as the collection responds to `count`.\n\n**NOTE:** In versions 4.4.0 and below, the `Rails::Pagination` module would end up included in `ActionController::Base` even if `ActionController::API` was defined. As of version 4.5.0, this is no longer the case. If for any reason your API controllers cannot easily changed be changed to inherit from `ActionController::API` instead, you can manually include the module:\n\n```ruby\nclass API::ApplicationController \u003c ActionController::Base\n  include Rails::Pagination\nend\n```\n\n## Grape\n\nWith Grape, `paginate` is used to declare that your endpoint takes a `:page` and `:per_page` param. You can also directly specify a `:max_per_page` that users aren't allowed to go over. Then, inside your API endpoint, it simply takes your collection:\n\n```ruby\nclass MoviesAPI \u003c Grape::API\n  format :json\n\n  desc 'Return a paginated set of movies'\n  paginate\n  get do\n    # This method must take an ActiveRecord::Relation\n    # or some equivalent pageable set.\n    paginate Movie.all\n  end\n\n  route_param :id do\n    desc \"Return one movie's cast, paginated\"\n    # Override how many Actors get returned. If unspecified,\n    # params[:per_page] (which defaults to 25) will be used.\n    # There is no default for `max_per_page`.\n    paginate per_page: 10, max_per_page: 200\n    get :cast do\n      paginate Movie.find(params[:id]).actors\n    end\n\n    desc \"Return one movie's awards, paginated\"\n    # Enforce max_per_page value will add the alowed values\n    # to the swagger docs, and cause grape to return an error\n    # if outside that range\n    paginate per_page: 10, max_per_page: 200, enforce_max_per_page: true\n    get :awards do\n      paginate Movie.find(params[:id]).awards\n    end\n  end\nend\n```\n\n## Headers\n\nThen `curl --include` to see your header-based pagination in action:\n\n```bash\n$ curl --include 'https://localhost:3000/movies?page=5'\nHTTP/1.1 200 OK\nLink: \u003chttp://localhost:3000/movies?page=1\u003e; rel=\"first\",\n  \u003chttp://localhost:3000/movies?page=173\u003e; rel=\"last\",\n  \u003chttp://localhost:3000/movies?page=6\u003e; rel=\"next\",\n  \u003chttp://localhost:3000/movies?page=4\u003e; rel=\"prev\"\nTotal: 4321\nPer-Page: 10\n# ...\n```\n\n## A Note on Kaminari and WillPaginate\n\napi-pagination requires either Kaminari or WillPaginate in order to function, but some users may find themselves in situations where their application includes both. For example, you may have included [ActiveAdmin][activeadmin] (which uses Kaminari for pagination) and WillPaginate to do your own pagination. While it's suggested that you remove one paginator gem or the other, if you're unable to do so, you _must_ configure api-pagination explicitly:\n\n```ruby\nApiPagination.configure do |config|\n  config.paginator = :will_paginate\nend\n```\n\nIf you don't do this, an annoying warning will print once your app starts seeing traffic. You should also configure Kaminari to use a different name for its `per_page` method (see https://github.com/activeadmin/activeadmin/wiki/How-to-work-with-will_paginate):\n\n```ruby\nKaminari.configure do |config|\n  config.page_method_name = :per_page_kaminari\nend\n```\n\n[activeadmin]: https://github.com/activeadmin/activeadmin\n[kaminari]: https://github.com/amatsuda/kaminari\n[will_paginate]: https://github.com/mislav/will_paginate\n\n[travis]: https://travis-ci.org/davidcelis/api-pagination\n[travis-badge]: http://img.shields.io/travis/davidcelis/api-pagination/master.svg\n[coveralls]: https://coveralls.io/r/davidcelis/api-pagination\n[coveralls-badge]: http://img.shields.io/coveralls/davidcelis/api-pagination/master.svg\n[code-climate]: https://codeclimate.com/github/davidcelis/api-pagination\n[code-climate-badge]: http://img.shields.io/codeclimate/github/davidcelis/api-pagination.svg\n[gemnasium]: http://gemnasium.com/davidcelis/api-pagination\n[gemnasium-badge]: http://img.shields.io/gemnasium/davidcelis/api-pagination.svg\n[gittip]: https://gittip.com/davidcelis\n[gittip-badge]: http://img.shields.io/gittip/davidcelis.svg\n","funding_links":[],"categories":["Ruby"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidcelis%2Fapi-pagination","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavidcelis%2Fapi-pagination","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidcelis%2Fapi-pagination/lists"}