Adding Pagination to an ActiveResource Backed Controller

We’re going to add Pagination to an ActiveResource model using activeresource-response for handling ActiveResource response headers on the ActiveResource server side of things and kaminari for handling the pagination on both the API and ActiveResource sides.

Adding pagination to your API

First we’ll want to install kaminari by adding it to our Gemfile and running bundle install.

There are two parts to paginating our index action on the API.

  1. Paginating our results so that our JSON response only contains the results for the page being requested.
  2. Returning the current page, per page, and total records data in the headers of our response so that we can correctly calculate and display the pagination information to the user in the ActiveResource server’s views.

Our code will look like this:

def index
  @foos = paginate(Foo.all)
  setup_headers(@foos)

  ...render @foos into JSON response...
end

private

def paginate(foos)
  foos.page(pagination_page).per(pagination_per_page)
end

def pagination_page
  params[:page] || 1
end

def pagination_per_page
  10
end

def setup_headers(foos)
  response.headers['X-total']  = foos.total_count.to_s
  response.headers['X-offset'] = foos.offset_value.to_s
  response.headers['X-limit']  = foos.limit_value.to_s
end

Paginating our results

Our pagination is handled in our paginate(foos) function. We call .page on our collection of records and pass in the page that is being requested. The page being requested is returned by pagination_page, this defaults to 1 if no page was passed in. This ensures that we are always paginating our responses. per(pagination_per_page) allows us to specify how many results should appear on each page. We could allow this to be set by the user like pages by adjusting pagination_per_page to be params[:per_page] || 10 however for our example we will fix it at 10.

Setting up our headers with pagination data

When we set up our pagination object on the ActiveResource server’s side of things we will need some way to know the total amount of records as well as what position in the collection the responded records are from and how many are per page. We provide this in our response by setting the X-total, X-offset, and X-limit headers respectively. This will allow us to construct a Kaminari Pagination object that we can use to insert pagination links into our views.

Adding Pagination to our ActiveResource Server

On the ActiveResource side of things there are two major things we need to set up:
1. Sending our pagination information to the API so it can correctly paginate and send back paginated data.
2. Rendering pagination links using the returned header information.

Sending Pagination Data to the API

With ActiveResource we can send parameters to the API by including them as options in our load request.

Normally when requesting a list of request with ActiveResource we’ll just call Foo.all. Requests like this also accept a list of parameters to send to the server. So by adding params: { page: params[:page] || 1 }) to our call so that it reads Foo.all(params: { page: params[:page] || 1 }) we can send our required page to the API and it will be available as params[:page] in our params on the API.

As we have already set up pagination on our API this will immediately take effect and you will start receiving paginated data matching the page you’ve requested.

Reading Response Headers from the API

To correctly install pagination we’ll need to load our paginated data into a Kaminari::PaginatableArray object so that we can insert it into our page along with a set of page links.

To do this we need to be able to access the headers that were set on the API. To do this we first need to install the activeresource-response gem and then add add_response_method :http_response to our Foo model.
This will add a method called http_response to our collection that will include the X-limit, X-total, and X-offset we responded with on the API.

Using the http_response method to gather the required data we can put together the paginatable_array like this:

@foos = Kaminari::PaginatableArray.new(foos, {
 limit: foos.http_response['X-limit'].to_i,
 offset: foos.http_response['X-offset'].to_i,
 total_count: foos.http_response['X-total'].to_i,
})

The above loads a paginatable array as if you had done the pagination locally using .page.

Rendering Pagination Links

Using the @foos paginatable array we can render pagination links in your view by calling <%= paginate @ foos %>.

What if we want to make pagination optional?

If you want to make pagination only apply if a page is passed into the endpoint then you should adjust your paginate function to return the full collection and not run setup_headers unless params[:page] is present. That way your endpoint will return all data if no page is passed in and paginate correctly if one is passed in.

What if we want to be able to customise our per_page figure?

If you want to allow your API to allow customisable page length you should adjust your pagination_per_page function to match your pagination_page function, like this:

def pagination_per_page
 params[:per_page] || 10
end

This way you default to 10 per page however have the option to allow users to set the per_page figure.

Thanks for subscribing!
Like what I'm doing? Subscribe and I'll let you know when I write new stuff.
Want to unsubscribe?
Subscribe