Routing in Rails

sjamescarter

Stephen Carter

Posted on July 10, 2023

Routing in Rails

Introduction

Routing is the means of navigating one or more networks. In full-stack Rails applications one routing system handles the full request/response cycle. In Rails API applications, separate routing systems handle the client and server. Routing is simplified when using RESTful conventions, and Rails provides a resources macro to further streamline the process. Custom routes fill the gaps conventions leave.

Rails Route Components

A Rails route has four components: verb, path, controller, and action. The syntax is as follows and written in the config/routes.rb file of a Rails application.

# app/config/routes.rb
Rails.application.routes.draw do

  verb '/path' to: 'controller#action'

end
Enter fullscreen mode Exit fullscreen mode

Verb

The first component of a route is an HTTP verb: GET, POST, PUT, PATCH, DELETE. Verbs communicate the type of request a client is sending to the server.

Path

The second route component is the path. A path communicates the resource endpoint the client request is accessing. In RESTful routes the path always include a plural endpoint: /users, /students, /goals. RESTful endpoints can also be followed with an id: /users/:id.

Controller

The third component is the controller. It follows the to: key and directs the request to the appropriate controller. In RESTful routes the endpoint and the controller are typically the same: '/students' to: 'students#action'. Just as RESTful endpoints are plural, controller names are plural as well.

Action

The fourth and final route component is the action. Actions are basically methods for controllers. This final element of the route sends the request to the appropriate action within the controller. In RESTful routes the action is associated with the HTTP verb.

# app/controllers/model_controller.rb
class ModelController < ApplicationController

  def action
    # response logic
  end 

end
Enter fullscreen mode Exit fullscreen mode

RESTful Routes

REST is short for REpresational State Transfer. Developed by Roy Fielding in 2000, REST is the standard for API communication. Using RESTful conventions simplifies the routing process and reduces the number of decisions that need to be made. It also makes the API accessible to any other developers.

CRUD

CRUD stands for Create, Read, Update, Delete. These are the four ways data is manipulated on the web. Each of these actions has an HTTP verb associated with it: GET, POST, PUT, PATCH, DELETE. Rails also has corresponding RESTful actions: index, create, show, update and destroy.

CRUD         HTTP           RAILS
-----------------------------------------
Create       POST           #create 
Read         GET            #index, #show
Update       PUT, PATCH     #update
Delete       DELETE         #destroy
Enter fullscreen mode Exit fullscreen mode

Index

The index action is associated with the GET verb and used to return all instances of a class.

# app/config/routes.rb
Rails.application.routes.draw do

  get '/books' to: 'books#index'

end

# app/controllers/books_controller.rb
class BooksController < ApplicationController

  def index
    books = Book.all
    render json: books
  end

end
Enter fullscreen mode Exit fullscreen mode

Create

The create action is associated with the POST verb and used to create a new instance of a class.

# app/config/routes.rb
Rails.application.routes.draw do

  post '/books' to: 'books#create'

end

# app/controllers/books_controller.rb
class BooksController < ApplicationController

  def create
    book = Book.create(params)
    render json: book, status: :created
  end

end
Enter fullscreen mode Exit fullscreen mode

Show

The show action is associated with the GET verb and used to return a specific instance of a class. The route must include an :id after the endpoint.

# app/config/routes.rb
Rails.application.routes.draw do

  get '/books/:id' to: 'books#show'

end

# app/controllers/books_controller.rb
class BooksController < ApplicationController

  def show
    book = Book.find(params[:id])
    render json: book
  end

end
Enter fullscreen mode Exit fullscreen mode

Update

The update action is associated with PATCH and PUT verbs and used to update a specific instance of a class. The route must include an :id after the endpoint.

# app/config/routes.rb
Rails.application.routes.draw do

  patch '/books/:id' to: 'books#update'
  put '/books/:id' to: 'books#update'

end

# app/controllers/books_controller.rb
class BooksController < ApplicationController

  def update
    book = Book.find(params[:id])
    book.update(params)
    render json: book, status: :accepted
  end

end
Enter fullscreen mode Exit fullscreen mode

Destroy

The destroy action is associated with the DELETE verb and used to destroy a specific instance of a class. The route must include an :id after the endpoint.

# app/config/routes.rb
Rails.application.routes.draw do

  delete '/books/:id' to: 'books#destroy'

end

# app/controllers/books_controller.rb
class BooksController < ApplicationController

  def destroy
    book = Book.find(params[:id])
    book.destroy
    render :no_content
  end

end
Enter fullscreen mode Exit fullscreen mode

Resources

Let's combine all the previous examples to see the routes and controller files when all RESTful routes are in use.

# app/config/routes.rb
Rails.application.routes.draw do

  get '/books' to: 'books#index'
  post '/books' to: 'books#create'
  get '/books/:id' to: 'books#show'
  patch '/books/:id' to: 'books#update'
  put '/books/:id' to: 'books#update'
  delete '/books/:id' to: 'books#destroy'

end

# app/controllers/books_controller.rb
class BooksController < ApplicationController
  def index
    books = Book.all
    render json: books
  end

  def create
    book = Book.create(params)
    render json: book, status: :created
  end

  def show
    book = Book.find(params[:id])
    render json: book
  end

  def update
    book = Book.find(params[:id])
    book.update(params)
    render json: book, status: :accepted
  end

  def destroy
    book = Book.find(params[:id])
    book.destroy
    render :no_content
  end
end
Enter fullscreen mode Exit fullscreen mode

That routes file is starting to look pretty lengthy and it only has routes for one resource. Imagine managing an app with 7 or 8 resources. Rails to the rescue! Rails has a resources macro which creates RESTful routes for all CRUD actions.

# app/config/routes.rb
Rails.application.routes.draw do

  resources :books

end
Enter fullscreen mode Exit fullscreen mode

So much better. Using resources not only cleans up and simplifies the code, it also prevents typos and syntax errors.

By default, resources creates seven routes for each resource. What happens when the controller only has one or two actions? The routes for the other actions have been created but will lead to routing error messages. To keep those routes from being created use the only: key.

resources :books, only: [:index, :show]
Enter fullscreen mode Exit fullscreen mode

Nested Routes

The resources macro can be used in nested routes as well. Nested routes reflect the relationships between models. If an Author model has_many :books, routes to the books through the authors can be established as such:

resources :authors do
  resources :books, only: [:index, :show]
end
Enter fullscreen mode Exit fullscreen mode

This declaration creates these nested routes:

get '/authors/:author_id/books' to: 'books#index'
get '/authors/:author_id/books/:id' to: 'books#show'
Enter fullscreen mode Exit fullscreen mode

Nested routes should only be one level deep.

Custom Routes

Custom routes follow the same format as RESTful routes. The only difference is there are no limitations of RESTful conventions. A good use-case for a custom route would be /login and /logout. These paths communicate useful information but they are not a resource in and of themselves. Since a sessions controller typically handles login and logout functionality, the routes could be setup as:

resources :sessions, only: [:create, :destroy]
Enter fullscreen mode Exit fullscreen mode

or as custom routes:

post '/login' to: 'sessions#create'
delete '/logout' to: 'sessions#destroy'
Enter fullscreen mode Exit fullscreen mode

Both routes work the same. The difference is on the client side. Fetching /login or /logout is really clear. While fetching /sessions is less clear.

Conclusion

Routing in Rails is pretty straightforward whether RESTful, nested or custom. All routes have four components: verb, path, controller, and action. For a deeper dive check out the docs.

Credit

Photo by Daniele Levis Pelusi on Unsplash

💖 💪 🙅 🚩
sjamescarter
Stephen Carter

Posted on July 10, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Routing in Rails
ruby Routing in Rails

July 10, 2023