Skip to content

Rails API Mode — Building RESTful JSON APIs

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Rails API Mode. We cover key concepts, practical examples, and best practices to help you master this topic.

Rails API mode strips unnecessary middleware for JSON-only applications, supporting versioning, serializers, CORS, rate limiting, error handling, and JSON API conventions.

What You'll Learn

By the end of this tutorial, you'll create an API-only Rails app, implement serializers, handle request validation, add CORS and rate limiting, version endpoints, and format JSON responses.

Why API Mode Matters

API mode removes session management, cookie handling, and view rendering for better performance. It focuses on request Parsing and JSON response generation.

Real-World Use

A mobile app backend uses Rails API with JSON API serializers, token authentication, rate limiting with Rack::Attack, and CORS for the web client.

API Path

flowchart LR
  A[Rails] --> B[API Mode]
  B --> C[Serializers]
  B --> D[Versioning]
  B --> E[CORS]
  B --> F[Error Handling]
  B --> G{You Are Here}
  style G fill:#f90,color:#fff

Creating an API App

Generate a new API-only Rails application.

rails new my_api --api
# config/application.rb
config.api_only = true
# Removes: views, cookies, session, flash, CSRF protection
# Keeps: JSON/XML rendering, HTTP caching, etags

API Versioning

Version your API routes.

# config/routes.rb
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :products
      resources :categories, only: [:index, :show]
    end
    namespace :v2 do
      resources :products
    end
  end
end

# app/controllers/api/base_controller.rb
module Api
  class BaseController < ApplicationController
    rescue_from ActiveRecord::RecordNotFound, with: :not_found
    rescue_from ActiveRecord::RecordInvalid, with: :unprocessable_entity

    private

    def not_found
      render json: { error: "Resource not found" }, status: :not_found
    end

    def unprocessable_entity(exception)
      render json: { errors: exception.record.errors.full_messages }, status: :unprocessable_entity
    end
  end
end

Serializers

Transform models to JSON.

# Gemfile
gem "jsonapi-serializer"
# app/serializers/product_serializer.rb
class ProductSerializer
  include JSONAPI::Serializer

  attributes :name, :price, :description, :published
  attribute :formatted_price do |product|
    number_to_currency(product.price)
  end
  attribute :created_at do |product|
    product.created_at.iso8601
  end

  belongs_to :category
  has_many :reviews
end

# Controller
class Api::V1::ProductsController < Api::BaseController
  def index
    products = Product.includes(:category).published
    render json: ProductSerializer.new(products, {
      params: { current_user: current_user },
      include: [:category],
    }).serializable_hash
  end
end

Rate Limiting

Protect API with rate limiting.

# Gemfile
gem "rack-attack"
# config/initializers/rack_attack.rb
class Rack::Attack
  limit = 100
  period = 1.minute

  throttle("api/ip", limit: limit, period: period) do |req|
    if req.path.start_with?("/api/")
      req.ip
    end
  end

  throttle("api/user", limit: 1000, period: 1.hour) do |req|
    if req.path.start_with?("/api/")
      req.env["HTTP_AUTHORIZATION"]
    end
  end

  self.throttled_responder = lambda do |env|
    headers = { "Retry-After" => period.to_s }
    [429, headers, [{ error: "Rate limit exceeded" }.to_json]]
  end
end

Common Mistakes

1. Using API Mode for Full-Stack Apps

API mode disables features needed for server-rendered HTML. Use full Rails for HTML apps.

2. Not Versioning APIs

API changes break clients. Version from the start, even for v1.

3. Exposing Internal Errors

Never include stack traces in API responses. Use rescue_from for consistent errors.

4. Skipping Request Validation

API requests need strong parameter validation like web forms.

5. Not Setting Proper HTTP Status Codes

Return 201 for created, 422 for validation errors, 401 for unauthorized, 403 for forbidden.

Practice Questions

1. What does config.api_only = true do?

Removes middleware for sessions, cookies, views, and CSRF protection.

2. Why should you version APIs?

To avoid breaking existing clients when the API changes.

3. What is a serializer for?

Transforming model data into JSON format suitable for API responses.

4. How do you rate limit API requests?

Use rack-attack gem with throttle rules based on IP or user token.

5. Challenge: Create an API endpoint with Serialization and error handling.

module Api
  module V1
    class ProductsController < BaseController
      before_action :authenticate

      def index
        products = Product.published.includes(:category)
        render json: ProductSerializer.new(products, include: [:category])
      end

      def show
        product = Product.find(params[:id])
        render json: ProductSerializer.new(product)
      end

      def create
        product = current_user.products.new(product_params)
        product.save!
        render json: ProductSerializer.new(product), status: :created
      end
    end
  end
end

FAQ

Can I use API mode with authentication?

Yes. Token-based authentication works well with API mode.

What is CORS and why is it needed?

Cross-Origin Resource Sharing allows web clients from different origins to access the API.

How do I handle JSON parse errors?

Rescue ActionDispatch::Http::Parameters::ParseError in the base controller.

Should I use API mode for a mobile backend?

Yes. API mode is designed for mobile and single-page application backends.

Can I add views later to an API app?

Yes. Remove config.api_only = true and add view-related gems.

Mini Project: Products API

Build a complete products API with versioning.

# config/routes.rb
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :products, only: [:index, :show, :create, :update]
      get "search/products", to: "products#search"
    end
  end
end

What's Next

Rails Serializers Rails Testing RSpec Rails Factory Bot

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro