Search
⌘K
    to navigateEnterto select Escto close

    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. Click on "Add a new book" and GET a form in response.
    4. POST the values from the form to create a new book.
    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 the book.

    These seven actions collectively are known as RESTful representations of the book. We can see that in all the above seven cases, sentences contain 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 '/books/:id/edit', to: "books#edit"
    7  put '/books/:id', 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, this is how the 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.

    Rails provides a Rake task to list all the routes.

    1bundle exec rails routes

    As we discussed that one single resources :books is responsible for generating these seven routes.

    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

    If we visit visit http://localhost:3000/rails/info/routes then also we can see all the routes.

    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 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
    Previous
    Next