August 06, 2024
Understanding Shallow Nested Routes in Rails
This post is part of my handbook on the Rails Router.
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 lessonedit: Given a lesson id, show the form to edit this lessonupdate: Given a lesson id, update this lessondestroy: 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.
Subscribe to get future posts via email (or grab the RSS feed)