ApiKit
A lightweight Rails toolkit for building standardized, structured API responses with serialization, error handling, filtering, sorting, and pagination.
Building clean, consistent APIs shouldn't be rocket science. ApiKit provides simple, powerful modules to get you up and running quickly.
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.