RailsApiKit
A lightweight Rails toolkit for building standardized, structured API responses with serialization, error handling, filtering, sorting, and pagination.
Features
ApiKit offers a collection of lightweight modules that integrate seamlessly with your Rails controllers:
- Object serialization - Powered by Active Model Serializers
- Error handling - Standardized error responses for parameters, validation, and generic errors
- Fetching - Support for relationship includes and sparse fieldsets
- Filtering & Sorting - Advanced filtering and sorting powered by Ransack
- Pagination - Built-in pagination support with links and metadata
Installation
Requirements:
- Ruby 3.3.0 or higher
- Rails 8.0 or higher
Add this line to your application's Gemfile:
gem "rails_api_kit"
And then execute:
$ bundle install
Quick Start
1. Enable Rails Integration
Add this to an initializer:
# config/initializers/rails_api_kit.rb
require "rails_api_kit"
ApiKit::RailsApp.install!
This registers the media type and renderers.
2. Basic Usage
class UsersController < ApplicationController
include ApiKit::Filtering
include ApiKit::Pagination
def index
allowed_fields = [ :first_name, :last_name, :created_at ]
api_filter(User.all, allowed_fields) do |filtered|
api_paginate(filtered.result) do |paginated|
render api_paginate: paginated
end
end
end
def show
user = User.find(params[:id])
render api: user
end
end
3. Create Serializers
class UserSerializer < ActiveModel::Serializer
attributes :id, :first_name, :last_name, :email, :created_at, :updated_at
has_many :posts
end
Core Modules
ApiKit::Filtering
Provides powerful filtering and sorting using Ransack:
class UsersController < ApplicationController
include ApiKit::Filtering
def index
allowed_fields = [ :first_name, :last_name, :email, :posts_title ]
api_filter(User.all, allowed_fields) do |filtered|
render api: filtered.result
end
end
end
Example requests:
# Filter by first name containing "John"
GET /users?filter[first_name_cont]=John
# Sort by last name descending, then first name ascending
GET /users?sort=-last_name,first_name
# Complex filtering with relationships
GET /users?filter[posts_title_matches_any]=Ruby,Rails&sort=-created_at
Sorting with Expressions
Enable aggregation expressions for advanced sorting:
def index
allowed_fields = [ :first_name, :posts_count ]
= { sort_with_expressions: true }
api_filter(User.joins(:posts), allowed_fields, ) do |filtered|
render api: filtered.result.group("users.id")
end
end
# Sort by post count
GET /users?sort=-posts_count_sum
ApiKit::Pagination
Handles pagination with standardized links:
class UsersController < ApplicationController
include ApiKit::Pagination
def index
api_paginate(User.all) do |paginated|
render api_paginate: paginated
end
end
private
def (resources)
{
pagination: (resources),
total: resources.respond_to?(:count) ? resources.count : resources.size
}
end
end
Example requests:
# Get page 2 with 20 items per page
GET /users?page[number]=2&page[size]=20
ApiKit::Fetching
Supports relationship inclusion and sparse fieldsets:
class UsersController < ApplicationController
include ApiKit::Fetching
def index
render api: User.all
end
private
def api_include
# Whitelist allowed includes
super & [ "posts", "profile" ]
end
end
Example requests:
# Include related posts
GET /users?include=posts
# Sparse fieldsets
GET /users?fields[user]=first_name,last_name
ApiKit::Errors
Standardized error responses for common Rails exceptions:
class UsersController < ApplicationController
include ApiKit::Errors
def create
user = User.new(user_params)
if user.save
render api: user, status: :created
else
render api_errors: user.errors, status: :unprocessable_entity
end
end
def update
user = User.find(params[:id])
if user.update(user_params)
render api: user
else
render api_errors: user.errors, status: :unprocessable_entity
end
end
private
def user_params
params.require(:data).require(:attributes).permit(:first_name, :last_name, :email)
end
def render_api_internal_server_error(exception)
# Custom exception handling (e.g., error tracking)
# Sentry.capture_exception(exception)
super(exception)
end
end
Handled exceptions:
StandardError→ 500 Internal Server ErrorActiveRecord::RecordNotFound→ 404 Not FoundActionController::ParameterMissing→ 422 Unprocessable Entity
Advanced Configuration
Custom Serializer Resolution
class UsersController < ApplicationController
def index
render api: User.all, serializer_class: CustomUserSerializer
end
private
def api_serializer_class(resource, is_collection)
ApiKit::RailsApp.serializer_class(resource, is_collection)
rescue NameError
"#{resource.class.name}Serializer".constantize
end
end
Custom Page Size
def api_page_size(pagination_params)
per_page = pagination_params[:size].to_i
return 30 if per_page < 1 || per_page > 100
per_page
end
Serializer Parameters
def api_serializer_params
{
current_user: current_user,
include_private: params[:include_private].present?
}
end
Configuration
Environment Variables
PAGINATION_LIMIT- Default page size (default: 30)
Dependencies
This gem leverages these excellent libraries:
- Active Model Serializers - Object serialization
- Ransack - Advanced filtering and sorting
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/iakbudak/api_kit
This project follows the Contributor Covenant code of conduct.
Development
After checking out the repo:
bundle install
bundle exec rspec # Run tests
bundle exec rubocop # Check code style
License
The gem is available as open source under the terms of the MIT License.