Rails Serializers — Transforming Models to JSON
In this tutorial, you will learn about Rails Serializers. We cover key concepts, practical examples, and best practices to help you master this topic.
Rails serializers transform ActiveRecord models to JSON following JSON API conventions, with support for attributes, relationships, conditional inclusion, and custom formatters.
What You'll Learn
By the end of this tutorial, you'll use JSONAPI::Serializer, define attributes and relationships, conditionally include fields, create custom formatters, and optimize serializer performance.
Why Serializers Matter
Serializers control the shape of API responses. They prevent exposing sensitive fields, format dates consistently, include related resources, and follow JSON API conventions.
Real-World Use
A product API uses ProductSerializer with name, price, and category. Admin users receive additional fields like cost and supplier. Reviews are included only when requested.
Serializers Path
flowchart LR
A[Rails API] --> B[Serializers]
B --> C[Attributes]
B --> D[Relationships]
B --> E[Conditional]
B --> F[Performance]
B --> G{You Are Here}
style G fill:#f90,color:#fff
JSONAPI::Serializer
Use the jsonapi-serializer gem.
# Gemfile
gem "jsonapi-serializer"
# app/serializers/product_serializer.rb
class ProductSerializer
include JSONAPI::Serializer
attributes :name, :description, :price, :sku, :published
attribute :formatted_price do |product|
sprintf("$%.2f", product.price)
end
attribute :created_at do |product|
product.created_at.iso8601
end
belongs_to :category, serializer: CategorySerializer
has_many :reviews, serializer: ReviewSerializer
has_many :variants
end
Conditional Attributes
Include attributes based on context.
class ProductSerializer
include JSONAPI::Serializer
attributes :name, :price, :description
# Conditional attribute based on params
attribute :cost_price, if: proc { |record, params|
params[:current_user]&.admin?
}
attribute :supplier_info, if: proc { |_record, params|
params[:include_supplier]
}
# Conditional relationship
has_many :reviews, if: proc { |_record, params|
params[:include_reviews]
}
end
Custom Formatters
Create custom attribute formatters.
class ProductSerializer
include JSONAPI::Serializer
attributes :name
attribute :price do |product|
{
amount: product.price.to_f,
currency: product.currency || "USD",
formatted: ActionController::Base.helpers.number_to_currency(product.price),
}
end
attribute :availability do |product|
if product.in_stock?
{ status: "in_stock", quantity: product.quantity }
elsif product.backorderable?
{ status: "backorder", estimated: product.restock_date&.iso8601 }
else
{ status: "out_of_stock" }
end
end
end
Pagination Meta
Include pagination metadata.
class ProductsController < ApplicationController
def index
products = Product.page(params[:page]).per(params[:per_page] || 20)
render json: ProductSerializer.new(products).serializable_hash.merge(
meta: {
current_page: products.current_page,
total_pages: products.total_pages,
total_count: products.total_count,
}
)
end
end
Common Mistakes
1. Serializing Sensitive Fields
Including password_hash, api_token, or internal IDs in serializers.
2. N+1 Queries via Serializer Relationships
Serializers access relationships that are not eager loaded. Use includes in queries.
3. Over-Serializing
Including every relationship and attribute slows responses. Include only what clients need.
4. Not Customizing Attribute Names
API consumers expect snake_case or camelCase. Customize attribute names for consistency.
5. Ignoring Serializer Cache
Serializers can be slow for large collections. Consider fragment Caching.
Practice Questions
1. What is a serializer used for?
Transforming models to JSON with controlled attribute and relationship inclusion.
2. How do you conditionally include an attribute?
Use the if: proc option on the attribute definition.
3. How do you include pagination metadata?
Merge meta hash into the serialized response.
4. How do you format an attribute value?
Use attribute :name do |record| block syntax.
5. Challenge: Create a serializer with conditional admin fields.
class UserSerializer
include JSONAPI::Serializer
attributes :id, :name, :email, :created_at
attribute :role, if: proc { |_user, params|
params[:current_user]&.admin?
}
attribute :last_login_at do |user|
user.last_sign_in_at&.iso8601
end
end
FAQ
Mini Project: Complete API Serialization
Build serializers for a blog API.
class PostSerializer
include JSONAPI::Serializer
attributes :title, :body, :slug, :published_at
attribute :excerpt do |post|
post.body.truncate(200)
end
belongs_to :author, serializer: UserSerializer
has_many :comments, serializer: CommentSerializer
has_many :tags do |post|
post.tags.order(:name)
end
end
What's Next
Rails Testing RSpec Rails Factory Bot Rails Faker
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro