Skip to Content

Rails

A Ruby on Rails integration gem for LightRate API throttling. This gem provides seamless integration with the LightRate API using controller before_actions to automatically throttle requests using local token buckets that automatically refill from the server when needed.

Features

  • Controller-Level Throttling: Use throttle_with_lightrate in any controller to enable automatic rate limiting
  • Flexible User Identification: Customize user identification per-controller with full access to controller context
  • Local Token Bucket Only: Uses only the local token bucket approach for efficient throttling with automatic server refills
  • Custom Exception Handling: Raises LightRateNoTokensAvailable when no tokens are available
  • Fine-Grained Control: Use :only or :except options to throttle specific actions
  • Controller Helpers: Manual token consumption methods for advanced use cases

Installation

Add this line to your application’s Gemfile:

gem 'lightrate-rails'

And then execute:

bundle install

Or install it yourself as:

gem install lightrate-rails

Configuration

Basic Setup

Required: Configure Lightrate in your Rails application initializer:

# config/initializers/lightrate_rails.rb LightrateRails.configure do |config| # Required: Your LightRate API key config.api_key = ENV['LIGHTRATE_API_KEY'] # Required: Your LightRate Application ID config.application_id = ENV['LIGHTRATE_APPLICATION_ID'] # Optional: Enable/disable throttling globally (default: true) config.enabled = Rails.env.production? # Optional: Default bucket size (default: 5) config.default_local_bucket_size = 10 end

Note: You must provide both a valid API key and Application ID. The gem will raise a ConfigurationError if either is missing.

Advanced Configuration

LightrateRails.configure do |config| # Required: Your LightRate API key config.api_key = ENV['LIGHTRATE_API_KEY'] # Required: Your LightRate Application ID config.application_id = ENV['LIGHTRATE_APPLICATION_ID'] # Enable/disable throttling globally (default: true) config.enabled = true # Default local bucket size (default: 5) config.default_local_bucket_size = 10 # API client configuration config.timeout = 30 config.retry_attempts = 3 config.logger = Rails.logger end

Usage

Global Client Architecture

The gem creates a single global LightRate client instance during Rails initialization. This approach provides several benefits:

  • Efficiency: No client creation overhead on each request
  • Shared Token Buckets: Token buckets are shared across all requests, providing better rate limiting
  • Memory Efficiency: Single client instance reduces memory usage
  • Consistent State: All parts of your application use the same client configuration

Controller-Level Throttling

The gem automatically includes LightrateRails::ControllerHelper in all your controllers. To enable throttling, simply call throttle_with_lightrate in any controller:

Basic Usage

# Throttle all actions in this controller class ApiController < ApplicationController throttle_with_lightrate end # Only throttle specific actions class UsersController < ApplicationController throttle_with_lightrate only: [:create, :update, :destroy] def index; end # NOT throttled def show; end # NOT throttled def create; end # Throttled def update; end # Throttled def destroy; end # Throttled end # Throttle all actions EXCEPT specific ones class PostsController < ApplicationController throttle_with_lightrate except: [:index, :show] def index; end # NOT throttled def show; end # NOT throttled def create; end # Throttled def update; end # Throttled def destroy; end # Throttled end # Disable throttling for this entire controller class HealthController < ApplicationController skip_lightrate_throttling def status; end # NOT throttled def ping; end # NOT throttled end

Custom User Identification

By default, the gem uses current_user.id to identify users. You can customize this per-controller:

# Use a different method on your user object class ApiController < ApplicationController throttle_with_lightrate user_identifier: -> { current_user&.uuid } end # Use a symbol for a controller method class ApiController < ApplicationController throttle_with_lightrate user_identifier: :get_api_user_id private def get_api_user_id request.headers['X-API-User-ID'] end end # Combine multiple identifiers class ApiController < ApplicationController throttle_with_lightrate user_identifier: -> { "#{current_user&.id}:#{current_organization&.id}" } end # Use API token for identification class ApiV2Controller < ApplicationController throttle_with_lightrate user_identifier: -> { current_api_user&.token } end # Use IP address for public endpoints class PublicApiController < ApplicationController throttle_with_lightrate user_identifier: -> { request.remote_ip } end # Combine with :only option class ComplexController < ApplicationController throttle_with_lightrate( only: [:create, :update], user_identifier: -> { request.headers['X-User-Token'] } ) end

Manual Token Management in Controllers

For advanced use cases, you can manually manage token consumption. The helper methods are automatically available in all controllers:

class ApiController < ApplicationController def path_specific_operation # Manually consume a token for a specific path consume_lightrate_token_for_path(path: '/api/v1/special') # Your operation here end def current_path_operation # Consume a token for the current request path (most common use case) consume_lightrate_token_for_current_path # Your operation here end def custom_user_operation # Use a different user identifier consume_lightrate_token_for_current_path( user_identifier: current_user.uuid ) # Your operation here end def specific_path_operation # Consume token for a specific path and method consume_lightrate_token_for_path( path: "/api/v1/special-endpoint", method: "POST", user_identifier: current_user.id ) # Your operation here end end

Controller Configuration Methods

Configure throttling behavior at the controller level:

MethodDescriptionOptions
throttle_with_lightrate(options = {})Enable throttling for this controlleronly: - Array of action names to throttle
except: - Array of action names to exclude
user_identifier: - Proc or Symbol for custom user identification
skip_lightrate_throttlingDisable throttling for this entire controllerNone

Available Helper Methods

The gem provides several helper methods for manual token management:

MethodDescriptionParameters
consume_lightrate_token_for_current_pathConsume token for current request pathuser_identifier
consume_lightrate_token_for_pathConsume token for specific pathpath, method, user_identifier

Most Common Use Cases:

  • consume_lightrate_token_for_current_path - Use this when you want to consume a token for the current request path
  • consume_lightrate_token_for_path - Use this for specific paths different from the current request

Exception Handling

The gem automatically handles rate limiting through controller callbacks:

  • HTML requests: Redirects with flash message
  • JSON requests: Returns 429 status with JSON error
  • XML requests: Returns 429 status with XML error

Customizing Throttling Responses

You can customize the throttling response by overriding the handle_rate_limit_exceeded method in your controllers:

class ApiController < ApplicationController throttle_with_lightrate private def handle_rate_limit_exceeded(exception) # Custom throttling response render json: { error: 'Rate Limited', message: 'You have exceeded the rate limit for this endpoint.', path: exception.path, user: exception.user_identifier, retry_after: 30 }, status: 429 end end

Exception Classes

LightRateNoTokensAvailable

Raised when no tokens are available for a request.

begin consume_lightrate_tokens(operation: 'my_operation') rescue LightrateRails::LightRateNoTokensAvailable => e puts "No tokens available for path: #{e.path}" puts "User: #{e.user_identifier}" puts "Response: #{e.response}" end

Configuration Options

OptionTypeDefaultDescription
api_keyStringnilRequired - Your LightRate API key
application_idStringnilRequired - Your LightRate Application ID
enabledBooleantrueEnable/disable throttling globally
default_local_bucket_sizeInteger5Default size for local token buckets
timeoutInteger30API request timeout in seconds
retry_attemptsInteger3Number of API retry attempts
loggerLoggerRails.loggerLogger for API requests
Last updated on