cyberangles guide

Creating RESTful APIs with Ruby on Rails

In today’s interconnected digital world, Application Programming Interfaces (APIs) serve as the backbone of communication between different software systems. RESTful APIs, which adhere to the principles of Representational State Transfer (REST), are the most popular choice for building scalable, maintainable, and user-friendly interfaces. Ruby on Rails (Rails), a powerful web application framework, simplifies API development with its "convention over configuration" philosophy, built-in tools, and robust ecosystem. This blog will guide you through creating a fully functional RESTful API using Rails. We’ll cover setup, resource modeling, CRUD operations, validation, authentication, testing, documentation, and deployment—equipping you with the skills to build production-ready APIs.

Table of Contents

  1. Setting Up a New Rails API Project
  2. Understanding RESTful Resources
  3. Generating Scaffold for CRUD Operations
  4. Customizing Endpoints
  5. Handling Parameters and Validation
  6. Authentication and Authorization
  7. Testing the API
  8. Documentation
  9. Deployment
  10. Conclusion
  11. References

1. Setting Up a New Rails API Project

Rails provides a built-in option to generate an API-focused application, skipping unnecessary frontend components (like views and assets). Let’s start by setting up the project.

Prerequisites

  • Ruby (3.0+ recommended)
  • Rails (7.0+ recommended)
  • PostgreSQL (or SQLite for development; PostgreSQL is preferred for production)

Step 1: Install Rails (if not already installed)

gem install rails  

Step 2: Create a New API Project

Use the --api flag to generate an API-only Rails app:

rails new blog_api --api -d postgresql  
cd blog_api  
  • --api: Configures Rails for API development (disables Action View, sprockets, etc.).
  • -d postgresql: Sets PostgreSQL as the database (replace with -d sqlite3 for SQLite).

Step 3: Configure CORS

APIs are often accessed by frontend apps on different domains. Enable Cross-Origin Resource Sharing (CORS) to allow this:

  1. Open config/initializers/cors.rb and uncomment the default configuration:
Rails.application.config.middleware.insert_before 0, Rack::Cors do  
  allow do  
    origins '*'  # Restrict to specific domains in production (e.g., 'https://your-frontend.com')  
    resource '*',  
      headers: :any,  
      methods: [:get, :post, :put, :patch, :delete, :options, :head]  
  end  
end  

Step 4: Set Up the Database

Initialize the PostgreSQL database:

rails db:create  

2. Understanding RESTful Resources

RESTful APIs organize data as “resources” (e.g., articles, users) and use HTTP methods to interact with them. Rails follows REST conventions by mapping CRUD operations to HTTP verbs and routes:

ActionHTTP MethodPurposeExample Route
indexGETList all resourcesGET /api/articles
showGETShow a single resourceGET /api/articles/1
createPOSTCreate a new resourcePOST /api/articles
updatePUT/PATCHUpdate a resourcePATCH /api/articles/1
destroyDELETEDelete a resourceDELETE /api/articles/1

3. Generating Scaffold for CRUD Operations

Rails scaffolding auto-generates models, controllers, and routes for a resource. Let’s build an API for managing Article resources (with title and body).

Step 1: Generate Scaffold

Run the scaffold generator:

rails generate scaffold Article title:string body:text  

This creates:

  • A model Article with title (string) and body (text).
  • A controller ArticlesController with RESTful actions (index, show, create, etc.).
  • Database migration for the articles table.

Step 2: Run the Migration

Execute the migration to create the articles table:

rails db:migrate  

Step 3: Inspect Routes

Check the generated routes with:

rails routes  

You’ll see RESTful routes for articles, prefixed with /articles by default. To namespace routes under /api (recommended for APIs), update config/routes.rb:

Rails.application.routes.draw do  
  namespace :api do  
    resources :articles  # Maps to /api/articles, /api/articles/1, etc.  
  end  
end  

Now, routes like GET /api/articles will trigger Api::ArticlesController#index.

Step 4: Update the Controller Namespace

Move app/controllers/articles_controller.rb to app/controllers/api/articles_controller.rb and update the class name:

class Api::ArticlesController < ApplicationController  
  # Scaffold-generated actions (index, show, create, etc.)  
end  

4. Customizing Endpoints

Sometimes you need non-standard endpoints (e.g., publish an article). Let’s add a custom action to mark an article as “published.”

Step 1: Add a published Attribute to Article

Generate a migration to add a published boolean column:

rails generate migration AddPublishedToArticles published:boolean:index  
rails db:migrate  

Set a default value for published (e.g., false) by updating the migration before running rails db:migrate:

def change  
  add_column :articles, :published, :boolean, default: false, null: false  
  add_index :articles, :published  
end  

Step 2: Add a Custom Route

Update config/routes.rb to define a publish action for individual articles:

namespace :api do  
  resources :articles do  
    member do  
      put :publish  # Maps to PUT /api/articles/:id/publish  
    end  
  end  
end  

Step 3: Implement the publish Action

Add the publish method to Api::ArticlesController:

def publish  
  @article = Article.find(params[:id])  
  @article.update(published: true)  
  render json: @article  
end  

5. Handling Parameters and Validation

Strong Parameters

Rails requires “strong parameters” to prevent mass assignment vulnerabilities. Permit only allowed attributes in the controller:

class Api::ArticlesController < ApplicationController  
  # ...  

  private  

  def article_params  
    params.require(:article).permit(:title, :body)  # Allow only :title and :body  
  end  
end  

Model Validation

Ensure data integrity with model validations. Update app/models/article.rb:

class Article < ApplicationRecord  
  validates :title, presence: true, length: { minimum: 5 }  
  validates :body, presence: true  
end  

Handle Validation Errors in Responses

Update the create and update actions to return meaningful errors when validation fails:

def create  
  @article = Article.new(article_params)  

  if @article.save  
    render json: @article, status: :created  # 201 Created  
  else  
    render json: { errors: @article.errors.full_messages }, status: :unprocessable_entity  # 422 Unprocessable Entity  
  end  
end  

6. Authentication and Authorization

Secure your API with token-based authentication. We’ll use Rails’ has_secure_token to generate unique tokens for users.

Step 1: Generate a User Model

rails generate model User email:string:index password_digest:string token:string:index  
rails db:migrate  
  • password_digest: For secure password storage (via has_secure_password).
  • token: For API authentication (via has_secure_token).

Step 2: Configure Authentication in User Model

Update app/models/user.rb:

class User < ApplicationRecord  
  has_secure_password  # Enables password hashing (requires bcrypt gem)  
  has_secure_token     # Generates a unique `token` attribute  

  validates :email, presence: true, uniqueness: true  
end  

Add bcrypt to your Gemfile (required for has_secure_password):

gem 'bcrypt'  

Run bundle install.

Step 3: Authenticate Requests

Add an authentication check to Api::ApplicationController (create this file if it doesn’t exist):

class Api::ApplicationController < ActionController::API  
  before_action :authenticate_user  

  private  

  def authenticate_user  
    token = request.headers['Authorization']&.split(' ')&.last  # Expecting "Bearer <token>"  
    @current_user = User.find_by(token: token)  

    return if @current_user  

    render json: { error: 'Unauthorized' }, status: :unauthorized  # 401 Unauthorized  
  end  
end  

Update Api::ArticlesController to inherit from Api::ApplicationController:

class Api::ArticlesController < Api::ApplicationController  
  # Actions will now require authentication  
end  

Step 4: Authorize Actions (Optional)

Ensure users can only modify their own articles. Add a user:references to Article:

rails generate migration AddUserToArticles user:references  
rails db:migrate  

Update Article and User models for associations:

# app/models/article.rb  
belongs_to :user  

# app/models/user.rb  
has_many :articles  

Modify ArticlesController to scope articles to the current user:

def index  
  @articles = @current_user.articles  # Only return the current user's articles  
  render json: @articles  
end  

def create  
  @article = @current_user.articles.new(article_params)  # Associate article with current user  
  # ...  
end  

7. Testing the API

Test your API using Rails’ built-in Minitest or RSpec. Here’s an example Minitest for the create action:

Step 1: Generate a Controller Test

rails generate test_controller api/articles  

Step 2: Write Tests

Update test/controllers/api/articles_controller_test.rb:

require 'test_helper'  

class Api::ArticlesControllerTest < ActionDispatch::IntegrationTest  
  setup do  
    @user = User.create(email: '[email protected]', password: 'password')  
    @auth_headers = { 'Authorization': "Bearer #{@user.token}" }  
  end  

  test "should create article" do  
    assert_difference('Article.count') do  
      post api_articles_url,  
        params: { article: { title: 'Test Title', body: 'Test body' } },  
        headers: @auth_headers,  
        as: :json  
    end  

    assert_response :created  
  end  

  test "should not create article with invalid params" do  
    post api_articles_url,  
      params: { article: { title: '', body: '' } },  
      headers: @auth_headers,  
      as: :json  

    assert_response :unprocessable_entity  
    assert_not_empty JSON.parse(response.body)['errors']  
  end  
end  

Step 3: Run Tests

rails test  

8. Documentation

Document your API so users know how to interact with it. Use RSwag to generate OpenAPI/Swagger docs.

Step 1: Add RSwag to Your Gemfile

group :development, :test do  
  gem 'rswag'  
  gem 'rswag-ui'  
  gem 'rswag-api'  
end  

Run bundle install and initialize RSwag:

rails generate rswag:install  

Step 2: Write Swagger Docs

Create a spec file (e.g., spec/swagger/articles_spec.rb) to document endpoints:

require 'swagger_helper'  

RSpec.describe 'API Articles', type: :request do  
  path '/api/articles' do  
    post 'Creates an article' do  
      tags 'Articles'  
      consumes 'application/json'  
      produces 'application/json'  

      parameter name: :article, in: :body, schema: {  
        type: :object,  
        properties: {  
          title: { type: :string },  
          body: { type: :string }  
        },  
        required: ['title', 'body']  
      }  

      response '201', 'article created' do  
        let(:article) { { title: 'Sample', body: 'Text' } }  
        run_test!  
      end  
    end  
  end  
end  

Step 3: Generate Docs

rails rswag  

Access docs at http://localhost:3000/api-docs.

9. Deployment

Deploy your API to a platform like Heroku:

Step 1: Prepare for Heroku

  1. Add Procfile to the root directory:
web: bundle exec puma -C config/puma.rb  
  1. Ensure Gemfile includes pg (PostgreSQL adapter) and rails_12factor (for Heroku):
gem 'pg', '~> 1.5'  
gem 'rails_12factor', group: :production  

Step 2: Deploy to Heroku

heroku create your-api-name  
git push heroku main  
heroku run rails db:migrate  

Your API will be live at https://your-api-name.herokuapp.com/api/articles.

10. Conclusion

Ruby on Rails simplifies building RESTful APIs with its conventions, built-in tools (e.g., scaffold, strong parameters), and ecosystem (e.g., RSwag for docs, bcrypt for auth). By following this guide, you’ve created a secure, testable API with CRUD operations, custom endpoints, and documentation.

11. References