Lightrate 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_lightratein 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
LightRateNoTokensAvailablewhen no tokens are available -
Fine-Grained Control: Use
:onlyor:exceptoptions 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 installOr install it yourself as:
$ gem install lightrate-railsConfiguration
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
endNote: 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
endUsage
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
endCustom 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
# Combine with :only option
class ComplexController < ApplicationController
throttle_with_lightrate(
only: [:create, :update],
user_identifier: -> { request.headers['X-User-Token'] }
)
endManual 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 expensive_operation
# Check if tokens are available for current path before proceeding
if lightrate_tokens_available?
# Your expensive operation here
perform_expensive_operation
else
render json: { error: 'Rate limit exceeded' }, status: 429
end
end
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 conditional_operation
# Use the helper to conditionally execute code
if lightrate_tokens_available?
# Execute expensive operation
heavy_computation
else
# Fallback to lighter operation
light_computation
end
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
endController Configuration Methods
Configure throttling behavior at the controller level:
| Method | Description | Options |
|---|---|---|
throttle_with_lightrate(options = {}) |
Enable throttling for this controller |
only: - Array of action names to throttleexcept: - Array of action names to excludeuser_identifier: - Proc or Symbol for custom user identification |
skip_lightrate_throttling |
Disable throttling for this entire controller | None |
Available Helper Methods
The gem provides several helper methods for manual token management. All methods share a single global LightRate client instance that is created during Rails initialization, ensuring efficient token bucket management across all requests.
| Method | Description | Parameters |
|---|---|---|
lightrate_tokens_available? |
Check if tokens are available for current path | user_identifier |
consume_lightrate_token_for_current_path |
Consume token for current request path | user_identifier |
consume_lightrate_token_for_path |
Consume token for specific path |
path, method, user_identifier
|
Most Common Use Cases:
-
lightrate_tokens_available?- Use this to check before expensive operations -
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
endException 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}"
endConfiguration Options
| Option | Type | Default | Description |
|---|---|---|---|
api_key |
String | nil |
Required - Your LightRate API key |
application_id |
String | nil |
Required - Your LightRate Application ID |
enabled |
Boolean | true |
Enable/disable throttling globally |
default_local_bucket_size |
Integer | 5 |
Default size for local token buckets |
timeout |
Integer | 30 |
API request timeout in seconds |
retry_attempts |
Integer | 3 |
Number of API retry attempts |
logger |
Logger | Rails.logger |
Logger for API requests |
Note: User identification is now configured per-controller using the user_identifier option in throttle_with_lightrate. See Custom User Identification for details.
Development
After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/lightbourne-technologies/lightrate-rails. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
License
The gem is available as open source under the terms of the MIT License.
Code of Conduct
Everyone interacting in the Lightrate Rails project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.