In Rails, in order to create routes we’ve define resources. Resources provide a mapping between an http verb with its URL and a controller action. For eg. to create routes for posts we’ve to add following to our routes.rb:

# routes.rb
Rails.application.routes.draw do
  ...

  resources :posts
end

This will generate the following seven routes each of it maps to PostsController and one of its action

HTTP Verb   Path             Controller#Action     Used for
GET         /posts           posts#index          display a list of all posts
GET         /posts/new       posts#new            return an HTML form for creating a new post
POST        /posts           posts#create         create a new post
GET         /posts/:id       posts#show           display a specific post
GET         /posts/:id/edit  posts#edit           return an HTML form for editing a post
PATCH/PUT   /posts/:id       posts#update         update a specific post
DELETE      /posts/:id       posts#destroy        delete a specific post

What are Nested Resources ?

There are cases when we’ve a resource is logically children another resource. For eg. comments are children of posts, a comment won’t exist without a post.

For such cases, nested resources are required. A nested resources can be defined as such:

# routes.rb
Rails.application.routes.draw do
  ...

  resources :posts do
    resources :comments
  end
end

This will create posts routes as defined earlier and following additional routes:

HTTP Verb   Path                               Controller#Action       Used for
GET         /posts/:post_id/comments           comments#index          display a list of all comments
GET         /posts/:post_id/comments/new       comments#new            return an HTML form for creating a new comment
POST        /posts/:post_id/comments           comments#create         create a new comment
GET         /posts/:post_id/comments/:id       comments#show           display a specific comment
GET         /posts/:post_id/comments/:id/edit  comments#edit           return an HTML form for editing a comment
PATCH/PUT   /posts/:post_id/comments/:id       comments#update         update a specific comment
DELETE      /posts/:post_id/comments/:id       comments#destroy        delete a specific comment

We can see here that :post_id is also available in the route with the comment’s :id. This can become cumbersome when there is deep nesting:

resources :parent_level do
  resources :child_level do
    resources :another_child_level
  end
end

# this will result in routes like
/parent_level/1/child_level/2/another_child_level/3

To avoid such scenarios atmost two level deep nesting is allowed. A better way to handle this is to use shallow nesting.

What’s Shallow Nesting ?

To avoid deep nesting as shown previously we must only nest the collection routes of the child. This will be done like this:

resources :posts do
  # collection routes of :comments resource
  resources :comments, only: [:index, :show, :new]
end
# member routes of :comments resource
resources :comments, only: [:create, :edit, :update, :delete]

This will result in following routes:

HTTP Verb   Path                               Controller#Action       Used for
GET         /posts/:post_id/comments           comments#index          display a list of all comments
GET         /posts/:post_id/comments/new       comments#new            return an HTML form for creating a new comment
POST        /posts/:post_id/comments           comments#create         create a new comment
GET         /comments/:id                      comments#show           display a specific comment
GET         /comments/:id/edit                 comments#edit           return an HTML form for editing a comment
PATCH/PUT   /comments/:id                      comments#update         update a specific comment
DELETE      /comments/:id                      comments#destroy        delete a specific comment

Here the last four are collection routes which does not contain :post_id anymore.

We can also just pass shallow: true when defining the :comments resource and that’ll generate the exact same routes:

# routes.rb
Rails.application.routes.draw do
  ...

  resources :posts do
    # Setting shallow: true tells Rails
    # to generate shallow nested routes
    resources :comments, shallow: true
  end
end