Learning how the rails routes are generated and how they map to HTTP methods and SQL queries is a foundational piece of knowledge that is missed by many developers when starting out. In this post we will explore some of the basics that will help fill in the gap so that students can understand Rails routes in the least amount of time.

Rails convention depict that a controller should only have 7 actions most of the time. Generally speaking if you are creating a controller or defining routes and you feel like you need something other than the 7 standard action you should consider creating a new controller.

In the rails router config/routes.rb we usually define routes using someting like resources :posts. This will generate the 7 default actions for us.

        posts GET    /posts(.:format)             posts#index
              POST   /posts(.:format)             posts#create
     new_post GET    /posts/new(.:format)         posts#new
    edit_post GET    /posts/:id/edit(.:format)    posts#edit
         post GET    /posts/:id(.:format)         posts#show
              PATCH  /posts/:id(.:format)         posts#update
              PUT    /posts/:id(.:format)         posts#update
              DELETE /posts/:id(.:format)         posts#destroy

Let's dissect this further and put it in the context of HTTP methods and SQL queries.

HTTP GET

Let's begin with the most basic HTTP method and that is the get method. Get is used to do exactly that to get data from the server. Any time you have to read any kind of data from the server remember that get is how you should do it. The data format does not matter. Whether you are getting html or json or xml data from your server at the end of the day http get is how you are going to do it. How does this map to Rails routes? Out of the 7 standard action in rails HTTP GET maps to 2 of the actions.

#index action

The index action is use to get a collection of items. It should also be the action to use if you need to filter or sort through the collection of data. Generally this maps to the SQL SELECT query, you will be generally selecting data from the database, to render out in your view of some kind. Since you are potentially retrieving 100s of records in one call it is generally improtant to keep the retrieved data simple, just enough to render out the view for the user to select. So maybe you would only render the title, author's name and the published date and maybe a bit of a blurb of the post to give enough context to the user.

Example screenshot from Rails Foundation video series example project

Generally we can pass parameters into the url of the GET request to modify how the the database will be queried. We usually see things like the following

https://www.codemy.net/posts?q=rails

In this case the q is the parameter name and the rails is the value. This value can be read by the controller and used to modify the database query.

#show action

The show action also uses HTTP GET to retrieve the data for an individual post. The difference between #index and #show is that the show action is retriving only one record. This means that we can load the object in depth. So If our data has deeply nested relationships the #show action would be where we would load the entire data structure for that object including all of the nested objects in the case of a blog, you would load the entire body, the comments the author's detail, and maybe even a 'related post'.

The #show action can generally display more information

When running rails routes command we can see the HTTP Method attached to the actions.

posts GET    /posts(.:format)                                posts#index
post  GET    /posts/:id(.:format)                            posts#show

So in generaly HTTP GET maps to #index and #show action which maps to SELECT SQL query. This is pretty much the basis of MVC.

The Controller receives the HTTP GET request and uses the Model to query the Database with the SELECT statement and finally sends the response back using the View. If you would like to see a more detailed explanation in a video check out the free episode below.

mvc-workflow-cover-1

HTTP POST

The HTTP POST method is generally used when the user needs to 'post' something to the server. This usually means the data inside the database will be modified in some way. There is no real convention with the HTTP Post some developers use POST to create / modify records in the database, but in Rails and many other frameworks with RESTful conventions POST usually maps to the #create action which also means that it maps to the SQL INSERT statement.

#create action

The create action will not accept query parameters instead any parameter to be processed by the server is passed in the body of the request. So for example form pameters are usually automagically processed by Rails and is exposed to us as  params in the controller.

Forms for creting new record maps to HTTP POST and is handled by #create action

We then need to sanitize this parameter to make sure nothing malicious is being passed in using something called 'strong parameters' which is explained further in the member exclusive episode below.

crud-operations-part-1-cover

If you run rails routes and find the HTTP POST method you will see something like this.

POST   /posts(.:format)                                   posts#create

Don't confuse the /posts with POST /posts is the path we might as well call the path something like /comments. It's the value we passed in when we defined our routes.

HTTP PUT / PATCH

HTTP PUT / PATCH is also used to create or update records in the database. However the main difference between PUT and POST is that PUT is meant to be idempotent. If you posted a form twice using POST it should create 2 records in the database however in a PUT / PATCH request if you submit the same form twice it should not modify the database a second time. This is what idempotent means. In Rails HTTP PUT / PATCH is mapped to the #update action, which is usually mapped to the SQL UPDATE statement.

#update action

The update action in rails will also accept the body and convert it into params for us in the controller. We can then pass that parameter through the same sanitization process and pass it into the model for updating our record in the database. There is 1 discernable difference between #create and #update, and that is the #update action requires that a record already exist in the database this is because the update action will usually reference the existing record via some kind of :id

Since HTML forms can only GET or POST we need an extra attribute

Above you'll see a form that will submit using the PATCH method. Since HTML forms can only submit GET or POST request we need some special help to manipulate the request to be a PUT / PATCH request. Note that the form is pre-filled with the value from the existing record in the database.

<input type='hidden' name='_method' value='patch' />

The line above helps rails know that this request is a PATCH request and we can also see the action of the form is submitting to /admin/posts/2 the number 2 in this case will map to the :id in our rails routes.

In this case we have a route that is under the admin namespace like so.

PATCH  /admin/posts/:id(.:format)                     admin/posts#update

If you would like to learn more about namespacing you can watch the following free Rails Foundation episode on Namespacing.

namespacing-our-controller-part-1-cover

HTTP DELETE

The last and final HTTP method the DELETE method is used to DELETE resources from our application. Usually the DELETE method maps to the #destroy action in the rails controller which in turns maps to the DELETE sql statement.

#destroy action

The DELETE action is alot like the PUT / PATCH http method in the sense that the HTML standard can't make that kind of request. It needs a little help from the framework.

Delete button renders the data method.

Rails will automatically map this button to the DELETE request which will invoke the #destroy action in the rails controller.

View Helpers

We might notice that when we actually invoked rails routes some of the routes have keywords next to them.

        posts GET    /posts(.:format)             posts#index
              POST   /posts(.:format)             posts#create
     new_post GET    /posts/new(.:format)         posts#new
    edit_post GET    /posts/:id/edit(.:format)    posts#edit
         post GET    /posts/:id(.:format)         posts#show
              PATCH  /posts/:id(.:format)         posts#update
              PUT    /posts/:id(.:format)         posts#update
              DELETE /posts/:id(.:format)         posts#destroy

The first routes have the posts keyword next to the route output, and you'll notice that the new, edit and show action have some kind of keywords next to them. This is so that we can use the helper method to generate these routes separately. In rails there are 2 main ways to use the helper. For example the new_post keyword can be used like this.

new_post_path => /posts/new
new_post_url  => http://localhost:3000/posts/new

Any time you see the keyword they map to the 2 helper methods with the path or url suffix. The PATCH / PUT , DELETE and POST methods don't have keywords next to them because they use the same one as the one above them. So for example to generate a POST url or path you can use posts_path and since the form will automatically submit using POST the server will know to map correctly to the #create action.

Summing Up

Understanding how Rails routing work can be of immense help in understanding the framework itself. Following the conventions and adhering to the principles set by the Rails framework can help us move faster in our development lifecycle.

We now also see that the order in which the rails routes command output the routes have a purpose it helps tell us which helper method we should use. So we need to 'read between the lines' a little bit to understand how it all works together  😜.


Some of the episodes mentioned in this post are from the Rails Foundation video series. If you are interested in learning rails or getting a deeper understanding of rails you can enjoy the entire series of 65 episodes for only $9 / month.