DEV Community

Tuyen Ha
Tuyen Ha

Posted on • Updated on

Nested Routing in Ruby

Why do we use Nested Routes?
In short, we use nested routes to make our codes cleaner which as a result makes them much easier to read. More importantly, nested routes make associating relationships less complex - nesting allows us to stay Restful and automatically routes the URL to where more than one model can be involved. Just by looking at how models are routed, it's clear which model belongs to which model and which models have many of which models.

As an example,
we have a user model that has many posts. By nesting the post model in the user model, you can route it like this:

/users/2/posts # all the posts for user with id:2

Using Nested Routes
To start we need to set up nested resources.

rails g resource model Blog title content:text
rails g resource model Comment reply blog:belongs_to
Enter fullscreen mode Exit fullscreen mode

The rails generate command, or rails g is a shortcut template to generate boilerplate code. By running rails g resource a migration file, model, controller, and serializer will automatically get generated for you without you having to do much of the work yourself. That line of code will also open up all of the routes in config/routes.rb for you as well.

Side note: Making use of these generators can save you so much time when working on Rails projects!

The blog model will contain a title as a string and content in text form content:text. When title without :string, terminal will automatically know that you want it as a string.

the blog:belongs_to line will generate a blog_id, which is a foriegn_key to blogs. This foreign key strongly enforces legitimate data at the DB level.

Next you create routes,

Rails.application.routes.draw do

  resources: blog 
  resources: comments

end
Enter fullscreen mode Exit fullscreen mode

Now we connect the parent and the child,

class Comment < ApplicationRecord
  belongs_to :blog
end
Enter fullscreen mode Exit fullscreen mode

This is saying that the comments belongs_to a blog. This relationship will allow the blog to have any number of comments.

Because the comment belongs to the blog, you must also specify in the blog model that a blog has many comments.

class Blog < ApplicationRecord
  has_many :comments, dependent: :destroy
end
Enter fullscreen mode Exit fullscreen mode

Keep in mind that the parent might get destroyed by certain events in the app and therefore we want to ensure that when the parent is deleted, all the children should be destroyed along with their parent components. Why? Because if it doesn't get destroyed all with its parent the belongs_to reference in the child object will have undesired consequences.

dependent: :destroy

this line will take care of that.

Now, the routes.rb file needs to be updated in order for the comment to be listed as a nested child of the blog.
This is done as follows,

Rails.application.routes.draw do
  resources: blogs do
     # nested resource for comments
     resources: comments, only: [:index, :show]
  end
  resources: :comments
end
Enter fullscreen mode Exit fullscreen mode

The 'index' and 'show' routes are nested routes and below the nesting are the regular resourced routes for comments. This allows the index action to deal with both the un-nested and nested routes.
/blogs/2/comments or /blogs/comments.

Although the comments are nested under the blogs we are still dealing with the comments.

If you type in rails routes in the terminal, you'll get a log of all the possible routes based on your routes.rb file. You'll be able to know for sure which routes are available.

Then you'll need to let the controller know about this change and tell it what to do.

class CommentController < ApplicationController
def index
   if params[:blog_id]
      @comments = Blog.find(params[:blog_id]).comments
   else
      @comments = Comment.all
   end
end

def show
   @comment = Comment.find(params[:id])
end
Enter fullscreen mode Exit fullscreen mode

I am telling the Comment Controller to render all comments under a specific blog "blog_id" when "/blogs/:id/comments" is being fetched.

The nested resources give us a way to document the parent/child relationship between our routes and our URLs.

Conclusion
Nested routes are great to use but keep in mind that nested routes can quickly become difficult to work with once the routes go beyond two layers deep. You can most definitely go beyond two layers deep but it's considered a code smell and best to be avoided.

Top comments (0)