Stephen Carter
Posted on July 10, 2023
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
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
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
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
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
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
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
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
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
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
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]
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
This declaration creates these nested routes:
get '/authors/:author_id/books' to: 'books#index'
get '/authors/:author_id/books/:id' to: 'books#show'
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]
or as custom routes:
post '/login' to: 'sessions#create'
delete '/logout' to: 'sessions#destroy'
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
Posted on July 10, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.