Non-Resourceful Custom Routes
A single call to
resources
in theroutes.rb
file declares the seven standard routes for your resource. **What if you need additional routes?*
As we saw in the previous lesson, resourceful routes in Rails provide you with a nice set of named routes which are mapped to the common, standardized URL patterns. However, there might be situations when you want to veer off the beaten path and create custom routes in addition to the RESTful ones.
Don't worry, Rails will let you create custom routes that don't fit one of the above seven routes with the member
and collection
routes. The member
route only applies to a single resource, whereas collection
will apply to a group of resources.
But first, do you really need them at all?
Before you continue, keep in mind that a better alternative to adding custom, non-resourceful routes is to introduce a new resourceful controller. Not only this keeps your code clean, but gives you better domain model.
Here's what David has to say on this:
What I’ve come to embrace is that being almost fundamentalistic about when I create a new controller to stay adherent to REST has served me better every single time. Every single time I’ve regretted the state of my controllers, it’s been because I’ve had too few of them. I’ve been trying to overload things too heavily.
David Heinemeier Hansson, Full Stack Radio
That said, reality is often messy, and Rails realizes this fact, providing you two ways to add custom, non-resourceful routes for your application.
Member Routes
Let's assume that your users need to see a preview of the course before purchasing it, and you need to expose that route on the courses/:id/preview
URL. How should you go about it?
Here's how you define the preview
route on the CoursesController
using the member
block.
resources :courses do
member do
get 'preview'
end
end
The router adds a new route that directs the request to the preview
action on the CoursesController
class. The remaining routes remain unchanged.
It also passes the course id in params[:id]
and creates the preview_course_path
and preview_course_url
helpers.
preview_course GET /courses/:id/preview(.:format) courses#preview
If you have a single member
route, use the short-hand version by passing the :on
option to the route, eliminating the block.
resources :courses do
get 'preview', on: :member
end
You can go one step further and leave out the :on
option.
resources :courses do
get 'preview'
end
It generates the following route.
➜ bin/rails routes -g preview
Prefix Verb URI Pattern Controller#Action
course_preview GET /courses/:course_id/preview(.:format) courses#preview
There are two important differences here:
- The course id is available as
params[:course_id]
instead ofparams[:id]
. - The route helpers changes from
preview_course_path
tocourse_preview_path
andpreview_course_url
tocourse_preview_url
.
Reminder: Although this approach works fine, a better way is to create a new controller dedicated to previewing images. Typically, you should strive to restrict your controllers with the seven default actions.
Collection Routes
Let's say you want to search all courses and you want to expose it on the courses/search
endpoint. You can add a new route for the collection of courses with the collection
block.
resources :courses do
collection do
get 'search'
end
end
This adds the following new route. It will also add a search_courses_path
and search_courses_url
helpers.
search_courses GET /courses/search(.:format) courses#search
If you don't need multiple collection
routes, just pass :on
option to the route.
resources :courses do
get 'search', on: :collection
end
This will add the same route as above.
Conclusion
Rails allows you to break out of its convention of using seven resourceful routes using the member
and collection
blocks. Both allow you to define additional routes for your resources than the standard seven routes.
A member
block acts on a single member of the resource, whereas a collection
operates on a collection of that resource. However, before you use a custom route, make sure if the problem cannot be solved by introducing a new resource.
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.