ruby 39 lines · 7 steps

Cursor-based pagination in a Rails controller

A Rails index action paginates articles by ID cursor instead of page numbers, keeping results stable and queries efficient.

Explained by highlit
1class ArticlesController < ApplicationController
2 DEFAULT_LIMIT = 25
3 MAX_LIMIT = 100
4 
5 def index
6 articles = Article.published.order(id: :desc).limit(page_limit)
7 articles = articles.where("articles.id < ?", cursor) if cursor
8 
9 records = articles.to_a
10 
11 render json: {
12 articles: records.map { |a| serialize(a) },
13 next_cursor: records.size == page_limit ? records.last.id : nil
14 }
15 end
16 
17 private
18 
19 def cursor
20 Integer(params[:cursor]) if params[:cursor].present?
21 rescue ArgumentError
22 nil
23 end
24 
25 def page_limit
26 limit = params.fetch(:limit, DEFAULT_LIMIT).to_i
27 limit = DEFAULT_LIMIT if limit <= 0
28 [limit, MAX_LIMIT].min
29 end
30 
31 def serialize(article)
32 {
33 id: article.id,
34 title: article.title,
35 slug: article.slug,
36 published_at: article.published_at.iso8601
37 }
38 end
39end
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Cursor pagination uses a stable ordering key like a descending ID instead of offsets, so results don't shift when rows are inserted.
  2. 2Returning next_cursor only when a full page was fetched gives clients a clean signal for when to stop.
  3. 3Sanitizing limit and cursor params up front protects the query from bad input and abusive page sizes.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Cursor-based pagination in a Rails controller — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code