Shallow Nested Routes in Rails

Understanding Shallow Nested Routes in Rails

3 min read
💡
This post is part of my handbook on the Rails Router.
The Rails Router
Everything you need to know about the incredible routing system in Rails.

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.

  • show: Given a lesson id, show this lesson
  • edit: Given a lesson id, show the form to edit this lesson
  • update: Given a lesson id, update this lesson
  • destroy: Given a lesson id, delete this lesson

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.

  • index: For which course we should show all the lessons?
  • create: Which course we should add this lesson to?
  • new: For which course we should add new lesson?

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

Shallow nesting lets you avoid deep nesting by generating the collection actions scoped under the parent, so as to get a sense of the hierarchy, but by not nesting the member actions. In other words, it only builds routes with the minimal amount of information to uniquely identify the resource.


That's a wrap. I hope you found this article helpful and you learned something new.

As always, 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. I reply to all emails I get from developers, and I look forward to hearing from you.

If you'd like to receive future articles directly in your email, please subscribe to my blog. Your email is respected, never shared, rented, sold or spammed. If you're already a subscriber, thank you.