Forum

Learn Ruby on Rails…
 
Notifications
Clear all

Learn Ruby on Rails REST API

1 Posts
1 Users
0 Reactions
9 Views
 josh
(@josh)
Member Admin
Joined: 2 months ago
Posts: 510
Topic starter  
# Ruby on Rails REST API: Complete Guide

Ruby on Rails is a powerful web framework that makes building RESTful APIs straightforward. Let me explain this step by step with practical examples.

## What is REST API?

REST (Representational State Transfer) is an architectural style for designing networked applications. It uses HTTP methods to perform CRUD operations:
- **GET**: Retrieve data
- **POST**: Create new data
- **PUT/PATCH**: Update existing data
- **DELETE**: Remove data

## Setting Up Rails API Project

```bash
# Install Rails
gem install rails

# Create a new API-only Rails application
rails new my_api --api
cd my_api

# Generate a model (example: User)
rails generate model User name:string email:string age:integer
rails db:migrate
```

## Basic Controller Structure

```ruby
# app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_action :set_user, only: [:show, :update, :destroy]

  # GET /users
  def index
    @users = User.all
    render json: @users
  end

  # GET /users/1
  def show
    render json: @user
  end

  # POST /users
  def create
    @user = User.new(user_params)
    
    if @user.save
      render json: @user, status: :created
    else
      render json: { errors: @user.errors }, status: :unprocessable_entity
    end
  end

  # PATCH/PUT /users/1
  def update
    if @user.update(user_params)
      render json: @user
    else
      render json: { errors: @user.errors }, status: :unprocessable_entity
    end
  end

  # DELETE /users/1
  def destroy
    @user.destroy
    head :no_content
  end

  private

  def set_user
    @user = User.find(params[:id])
  end

  def user_params
    params.require(:user).permit(:name, :email, :age)
  end
end
```

## Routes Configuration

```ruby
# config/routes.rb
Rails.application.routes.draw do
  # RESTful routes for users
  resources :users
  
  # Or manually define routes
  # get '/users', to: 'users#index'
  # post '/users', to: 'users#create'
  # get '/users/:id', to: 'users#show'
  # patch '/users/:id', to: 'users#update'
  # put '/users/:id', to: 'users#update'
  # delete '/users/:id', to: 'users#destroy'
end
```

## Advanced Example: Blog API

```ruby
# app/models/post.rb
class Post < ApplicationRecord
  belongs_to :user
  has_many :comments, dependent: :destroy
  
  validates :title, presence: true
  validates :content, presence: true
end

# app/models/user.rb
class User < ApplicationRecord
  has_many :posts, dependent: :destroy
  has_many :comments, dependent: :destroy
  
  validates :name, presence: true
  validates :email, presence: true, uniqueness: true
end

# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :set_post, only: [:show, :update, :destroy]
  before_action :authenticate_user!, except: [:index, :show]

  # GET /posts
  def index
    @posts = Post.includes(:user).all
    render json: @posts, include: [:user, :comments]
  end

  # GET /posts/1
  def show
    render json: @post, include: [:user, :comments]
  end

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

  # PATCH/PUT /posts/1
  def update
    if @post.update(post_params)
      render json: @post
    else
      render json: { errors: @post.errors }, status: :unprocessable_entity
    end
  end

  # DELETE /posts/1
  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, :content, :published)
  end
end
```

## API Versioning Example

```ruby
# config/routes.rb
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :users
      resources :posts
    end
    
    namespace :v2 do
      resources :users do
        member do
          patch :activate
        end
      end
      resources :posts
    end
  end
end

# Usage:
# GET /api/v1/users
# POST /api/v2/users
```

## Error Handling and Response Formatting

```ruby
# app/controllers/application_controller.rb
class ApplicationController < ActionController::API
  rescue_from ActiveRecord::RecordNotFound, with: :not_found
  rescue_from ActiveRecord::RecordInvalid, with: :unprocessable_entity
  
  private

  def not_found(exception)
    render json: { error: exception.message }, status: :not_found
  end

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

# Custom JSON response formatting
class PostsController < ApplicationController
  def index
    @posts = Post.page(params[:page]).per(10)
    
    render json: {
      data: @posts,
      meta: {
        current_page: params[:page] || 1,
        per_page: 10,
        total: Post.count
      }
    }
  end
end
```

## Authentication Example (JWT)

```ruby
# Gemfile
gem 'jwt'
gem 'bcrypt'

# app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
  def create
    user = User.find_by(email: params[:email])
    
    if user && user.authenticate(params[:password])
      token = JWT.encode(
        { user_id: user.id, exp: 24.hours.from_now.to_i },
        Rails.application.secret_key_base,
        'HS256'
      )
      
      render json: {
        token: token,
        user: user
      }
    else
      render json: { error: 'Invalid credentials' }, status: :unauthorized
    end
  end
end

# Authentication concern
# app/controllers/concerns/authentication.rb
module Authentication
  extend ActiveSupport::Concern

  private

  def authenticate_user!
    token = request.headers['Authorization']&.split(' ')&.last
    decoded_token = JWT.decode(token, Rails.application.secret_key_base, true, algorithm: 'HS256')
    user_id = decoded_token[0]['user_id']
    @current_user = User.find(user_id)
  rescue JWT::DecodeError, ActiveRecord::RecordNotFound
    render json: { error: 'Unauthorized' }, status: :unauthorized
  end
end
```

## Testing Your API

```ruby
# test/controllers/users_controller_test.rb
require 'test_helper'

class UsersControllerTest < ActionDispatch::IntegrationTest
  setup do
    @user = users(:one)
  end

  test "should get index" do
    get users_url, as: :json
    assert_response :success
  end

  test "should create user" do
    assert_difference('User.count') do
      post users_url, params: { user: { name: "John Doe", email: "john@example.com", age: 30 } }, as: :json
    end

    assert_response 201
  end

  test "should show user" do
    get user_url(@user), as: :json
    assert_response :success
  end
end
```

## API Usage Examples

### Create a User (POST)
```bash
curl -X POST  http://localhost:3000/users  \
  -H "Content-Type: application/json" \
  -d '{
    "user": {
      "name": "John Doe",
      "email": "john@example.com",
      "age": 30
    }
  }'
```

### Get All Users (GET)
```bash
curl  http://localhost:3000/users 
```

### Update a User (PUT/PATCH)
```bash
curl -X PATCH  http://localhost:3000/users/1  \
  -H "Content-Type: application/json" \
  -d '{
    "user": {
      "name": "John Smith",
      "age": 31
    }
  }'
```

### Delete a User (DELETE)
```bash
curl -X DELETE  http://localhost:3000/users/1 
```

## Key Features of Rails API

1. **Built-in JSON Serialization**: Automatic conversion to JSON
2. **RESTful Routing**: Convention-based routing
3. **Strong Parameters**: Security against mass assignment
4. **ActiveRecord Integration**: Database operations
5. **Middleware Support**: Custom middleware for authentication, logging
6. **Testing Framework**: Built-in testing capabilities

This setup provides a robust foundation for building REST APIs with Ruby on Rails, handling all the common CRUD operations efficiently.

   
Quote
Share: