Skip to content

Ror Api

DodaTech 4 min read

title: Ruby on Rails API — Complete Guide to Building RESTful APIs description: 'Learn Ruby on Rails API: API-only mode, serializers (jbuilder, active_model_serializers), JWT auth, versioning, rate limiting, and JSON API conventions.' date: 2026-06-28 lastmod: 2026-06-28 weight: 25 tags: [backend, ror]


Rails API applications serve JSON responses to client applications (mobile apps, SPAs) using API-only mode, serializers for JSON formatting, and token-based authentication.

## What You'll Learn

By the end of this tutorial, you'll create API-only Rails apps, serialize JSON responses with Jbuilder, implement JWT authentication, version APIs, handle errors, and follow JSON API conventions.

## Real-World Use

A Rails API powers a mobile shopping app. Products, cart, and orders are served as JSON. Mobile clients authenticate via JWT tokens. The API serves 10,000+ requests per minute.

## API Learning Path

```mermaid
flowchart LR
  A[Authorization] --> B[API]
  B --> C[Testing]
  C --> D[Asset Pipeline]
  D --> E[Mailers]
  B --> F{You Are Here}
  style F fill:#f90,color:#fff

API-Only Rails

rails new my_api --api
# Creates a minimal app without views, cookies, or asset pipeline
# config/application.rb
class Application < Rails::Application
  config.api_only = true
end

API Controller

class Api::V1::PostsController < ApplicationController
  before_action :authenticate_user!
  before_action :set_post, only: [:show, :update, :destroy]

  def index
    @posts = Post.recent
    render json: @posts
  end

  def show
    render json: @post
  end

  def create
    @post = current_user.posts.build(post_params)
    if @post.save
      render json: @post, status: :created
    else
      render json: { errors: @post.errors.full_messages }, status: :unprocessable_entity
    end
  end

  def update
    if @post.update(post_params)
      render json: @post
    else
      render json: { errors: @post.errors.full_messages }, status: :unprocessable_entity
    end
  end

  def destroy
    @post.destroy
    head :no_content
  end

  private

  def set_post
    @post = Post.find(params[:id])
  end

  def post_params
    params.require(:post).permit(:title, :body)
  end
end

JWT Authentication

gem "jwt"
bundle install
class Api::V1::AuthController < ApplicationController
  def login
    user = User.find_by(email: params[:email])
    if user&.authenticate(params[:password])
      token = JWT.encode(
        { user_id: user.id, exp: 24.hours.from_now.to_i },
        Rails.application.credentials.secret_key_base,
        "HS256"
      )
      render json: { token: token, user: { id: user.id, email: user.email } }
    else
      render json: { error: "Invalid credentials" }, status: :unauthorized
    end
  end
end

class ApplicationController < ActionController::API
  def authenticate_user!
    token = request.headers["Authorization"]&.split(" ")&.last
    return render json: { error: "Unauthorized" }, status: :unauthorized unless token

    decoded = JWT.decode(token, Rails.application.credentials.secret_key_base, true, algorithm: "HS256")
    @current_user = User.find(decoded[0]["user_id"])
  rescue JWT::DecodeError, ActiveRecord::RecordNotFound
    render json: { error: "Unauthorized" }, status: :unauthorized
  end
end

Serialization with Jbuilder

# app/views/api/v1/posts/index.json.jbuilder
json.array! @posts do |post|
  json.id post.id
  json.title post.title
  json.excerpt truncate(post.body, length: 100)
  json.author do
    json.id post.user.id
    json.name post.user.name
  end
  json.comments_count post.comments.count
  json.created_at post.created_at.iso8601
end
# Controller
def index
  @posts = Post.includes(:user).recent
  # Renders index.json.jbuilder automatically
end

API Versioning

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

Common Mistakes

1. Not Using API-Only Mode

Full Rails includes views, cookies, and assets. Use --api flag to avoid unnecessary middleware.

2. Exposing Internal Errors

Always return consistent error responses. Never expose stack traces or validation details in production.

3. No Rate Limiting

Without rate limiting, a single client can overwhelm the API. Use rack-attack gem.

4. Not Versioning APIs

Breaking changes break existing clients. Version from the start (v1, v2).

5. Missing CORS Configuration

Browser-based clients need CORS headers. Include rack-cors gem and configure allowed origins.

Practice Questions

1. What is Rails API-only mode?

A minimal Rails configuration without views, cookies, sessions, and asset pipeline. Ideal for JSON APIs.

2. How do you implement JWT auth in Rails?

Encode a JWT with user_id and expiration on login. Validate the token on each request in a before_action.

3. What is Jbuilder?

A templating gem for building JSON responses using Ruby DSL. Views are .json.jbuilder files.

4. How do you handle API errors?

Return consistent JSON error objects with appropriate HTTP status codes (422, 401, 403, 404).

5. Challenge: Create an API-only Rails app with JWT auth and a posts endpoint.

rails new blog_api --api
# Add JWT gem, create AuthController with login
# Create PostsController with CRUD
# Protect with authenticate_user! before_action

FAQ

Should I use Jbuilder or Active Model Serializers?

Jbuilder is built-in and flexible. AMS is good for complex serialization. Both work well.

How do I handle pagination in API?

Use pagy or kaminari gem. Include page, per_page, total, and total_pages in response metadata.

What is rack-cors?

A middleware gem that sets CORS headers. Configure allowed origins, methods, and headers.

How do I test API endpoints?

Use request specs (RSpec) or integration tests. Test each endpoint with valid/invalid auth and data.

Can I use Devise for API auth?

Yes. Devise with JWT gem (devise-jwt) provides token-based authentication with Devise's features.

Mini Project: JSON API

Create a Rails API with posts, JWT auth, and Jbuilder serialization.

rails new blog_api --api
# rails generate scaffold Api::V1::Post title body --no-template-engine
# Add JWT auth
# Add Jbuilder views
# Test with curl

What's Next

Ruby on Rails Testing Ruby on Rails Asset Pipeline

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro