Learn Ruby on Rails Book

RESTful routes in Ruby on Rails

Let's say that we have a library full of books and we need to build an application to manage these books. Typically we need to perform the following seven operations.

  1. GET the list of all books.
  2. GET detailed information about a particular book.
  3. GET a form with the default values pre-filled for creating a new book.
  4. POST the values from the form to create a new book record.
  5. GET the form with an existing book's data prepopulated so that the user can edit the information about the book.
  6. UPDATE the system with the edited data that was just submitted.
  7. DELETE one particular book.

These seven actions collectively are known as RESTful representations of the book. We can see that in all the above seven cases, sentences start with the HTTP verbs GET or POST except for operation numbers 6 and 7.

In operation number 6, we are trying to update a particular book. It is possible to use the POST method for updating the records. But we will be following the Rails' standards and will use PUT or PATCH.

The routes for all the seven actions will look like this:

1Rails.application.routes.draw do
2  get '/books',       to: "books#index"
3  get 'books/:id',    to: "books#show"
4  get 'books/new',    to: "books#new"
5  post 'books',       to: "books#create"
6  get 'book/edit',    to: "books#edit"
7  put 'book/update',  to: "books#update"
8  delete 'books/:id', to: "books#destroy"
9end

Rails resources

Writing all those seven lines of code in the routes file time and again for each item gets repetitive. Rails has put all that under the umbrella term of resources.

If we use resources instead of manually specifying all routes, here is how our previous routes definition will look like:

1Rails.application.routes.draw do
2  resources :books
3end

Think of resources as a shortcut for not typing all that code. Looking into the mapper.rb file from Rails codebase, we can see that all the seven methods are listed there.

You can use this command to list all routes available in your application.

1bundle exec rake routes

If you replace the config/routes.rb file with our previous code and run the routes list command, you will get an output similar to this:

PrefixVERBURI PatternController#ActionUsed for
booksGET/booksbooks#indexList books
POST/booksbooks#createCreate a book
new_bookGET/books/newbooks#newForm for a new book
edit_bookGET/books/:idbooks#editForm for editing book
bookGET/books/:idbooks#showShow info about book
PATCH/books/:idbooks#updateUpdate info about book
PUT/books/:idbooks#updateUpdate info about book
DELETE/books/:idbooks#destroyDelete info about book

We will discuss about the column named Prefix from the above table, in a different chapter.

Alternatively, you can visit http://localhost:3000/rails/info/routes to list all the available routes in our application in a GUI way. More than that, it offers search and filter options too.

Using only and except to be selective

Sometimes we do not need all the seven routes. For example, let's say that in our application, we don't need to allow users to delete any book. In that case, even if a user sends a DELETE request, we don't want the destroy action to be called.

In order to do that, we need to tell Rails Routing not to have any routing for DELETE verb:

1resources :books, except: [:destroy]

Here is another example:

1resources :books, only: [:index, :show]

In this case, only index and show actions are added to routing. That is, a user can see the list of books and can get details about a book. But they can't create, edit or delete a book.

Difference between singular resource and resources in Rails routes

So far, we have been using resources. Rails also provides a singular version of it called resource.

Rails recommends us to use singular resource when we do not have an identifier. For example, the URL for the profile page is, typically, /profile and not /profile/495. In such cases, we do not need certain routes.

For example, if we define a resource like this in config/routes.rb:

1resource :profile

The routes generated by singular resource will be:

PrefixVERBURI PatternController#Action
new_profileGET/profile/newprofiles#new
edit_profileGET/profile/editprofiles#edit
profileGET/profileprofiles#show
PATCH/profileprofiles#update
PUT/profileprofiles#update
DELETE/profileprofiles#destroy
POST/profileprofiles#create

Notice that the URL pattern is using the singular style. URLs are /profile/new and not /profiles/new.

However, the controller name is still plural. This was a subject of great debate within the Rails community. The community has decided to keep the controller name plural for both singular and multiple resources.

Here is an explanation from the Rails official guide:

Because you might want to use the same controller for a singular route (/account) and a plural route (/accounts/45), singular resources map to plural controllers. So that, for example, resource :photo and resources :photos creates both singular and plural routes that map to the same controller (PhotosController).

Notice that for the show action, the corresponding URL is just /profile. No identifier is needed. The same is true for creating, updating, and deleting the profile.

In this case, for all these actions, we will be performing the operations on the currently logged-in user's data. Therefore we don't need an identifier for those actions.

Finally, there is no index action since here we are talking about a singular resource.

Another common usage of the singular resource is when we are dealing with login operations. When a user logs in, we need to maintain a session. For each logged-in user, there could be only one session.

In that case, our routes definition will look like this:

1resource :session, only: [:new, :create, :destroy]

Controller could look like this:

1class SessionsController < ApplicationController
2  def new
3    # logic
4  end
5
6  def create
7    # logic
8  end
9
10  def destroy
11    # logic
12  end
13end

There shouldn't be any changes to the application in this chapter. So let us clean up any accidental changes.

1git clean -fd
⌘K
    to navigateEnterto select Escto close
    Previous
    Next