Rails 7.2 introduced a built-in rate limiting and now the next version of Rails has a few nice improvements lined up.

Historically we would use rack-attack for rate limiting, but Rails 7.2 introduced a built-in rate limiter:

class SignupController < ApplicationController
  rate_limit to: 5, within: 1.minute

  def create
    render plain: "Signed up"
  end
end

In the upcoming Rails 8.2, two new features will be quite useful. I had mentioned them in my previous blog on rate limiting, but let us look at them in detail here.

Dynamic duration and window

Pull request

Previously the duration to: and window within: parameters only accepted fixed values. Now they can be a callable (method name, proc or lambda) allowing dynamic values:

class EmployeesController < ApplicationController
  rate_limit to: :max_requests, within: :max_duration

  def create
    render plain: "Employee created"
  end

  private

  def max_requests
    current_user.admin? ? 1000 : 5
  end

  def max_duration
    current_user.admin? ? 1.hour : 1.minute
  end
end

This allows us to change the rate limit based on business logic.

Duck-typing cache-key object

Pull request

Default rate limit is on remote_ip. Now you can pass object which responds to cache_key:

class User < ApplicationRecord
  def cache_key
    "user/#{id}"
  end
end
class ReportsController < ApplicationController
  rate_limit to: 20, within: 1.minute, by: -> { current_user }
end

This implements a per-user rate limit.

All the code code and tests can be found in this gist.