Shallow Nested Routes

In the previous post, we learned how nested resources work. Consider the following route configuration:

# config/routes.rb
resources :courses do
  resources :lessons
end

It generates the following routes:

$ bin/rails routes -g lesson
                Prefix Verb   URI Pattern                                    Controller#Action
        course_lessons GET    /courses/:course_id/lessons(.:format)          lessons#index
                       POST   /courses/:course_id/lessons(.:format)          lessons#create
     new_course_lesson GET    /courses/:course_id/lessons/new(.:format)      lessons#new
    edit_course_lesson GET    /courses/:course_id/lessons/:id/edit(.:format) lessons#edit
         course_lesson GET    /courses/:course_id/lessons/:id(.:format)      lessons#show
                       PATCH  /courses/:course_id/lessons/:id(.:format)      lessons#update
                       PUT    /courses/:course_id/lessons/:id(.:format)      lessons#update
                       DELETE /courses/:course_id/lessons/:id(.:format)      lessons#destroy

You must have noticed that the show, edit, update, and destroy routes for a lesson don't really need the course. They can exist on their own.

In contrast, the index, create, and new routes for a lesson will always need a course, otherwise Rails won't know which course we're talking about.

To address this, we could define our nested routes like this:

resources :courses do
  resources :lessons, only: [:index, :new, :create]
end

resources :lessons, only: [:show, :edit, :update, :destroy]

However, Rails provides the shallow option as a shortcut.

resources :courses do
  resources :lessons, shallow: true
end

Shallow nesting strikes a balance between descriptive routes and deep nesting.

Let's review the generated routes.

$ bin/rails routes -g lesson
                Prefix Verb   URI Pattern                               Controller#Action
## Need a Course                
        course_lessons GET    /courses/:course_id/lessons(.:format)     lessons#index
                       POST   /courses/:course_id/lessons(.:format)     lessons#create
     new_course_lesson GET    /courses/:course_id/lessons/new(.:format) lessons#new
## Doesn't Need a Course
           edit_lesson GET    /lessons/:id/edit(.:format)               lessons#edit
                lesson GET    /lessons/:id(.:format)                    lessons#show
                       PATCH  /lessons/:id(.:format)                    lessons#update
                       PUT    /lessons/:id(.:format)                    lessons#update
                       DELETE /lessons/:id(.:format)                    lessons#destroy

If you have multiple resources nested under a single parent resource, you can pass the shallow option to the parent.

resources :courses, shallow: true do
  resources :lessons
  resources :students
end

If you have any questions or feedback, didn't understand something, or found a mistake, please leave a comment below or send me an email. You can also subscribe to my blog to receive future posts via email.