atomaka.com/_posts/2016-11-18-singular-and-plural-rails-routes-for-the-same-resource.md

3.1 KiB
Raw Permalink Blame History

layout title tag
post Singular and Plural Rails Routes for the Same Resource
api
ruby-on-rails
restful

Sometimes when building your API with Rails, following best practices may seem difficult. I recently came across one of these cases and was tempted to take a shortcut. However, I held strong and eventually came to a quality RESTful solutionbut not without issue.

I wanted to allow users of my API to quickly access their own user profile. Our application had already implemented a route to allow this via GET /profiles/:id and simply implemented with resources :profiles, only: [:show] in our routes configuration.

Unfortunately, our API users wanted to be able to access their profile without providing their ID. My first pass at resolving this was passed in a "fake resource" to accomplish this.

{% gist 1a056f8dca8931d8872c6cfefebb2d1a %}

But I had broken one of the RESTful best practices. /profiles/me is not an actual resource but we are pretending it is. So I looked to the Rails routes documentation for guidance and came across singular resources.

Sometimes, you have a resource that clients always look up without referencing an ID. For example, you would like /profile to always show the profile of the currently logged in user.

I should not have been surprised that my exact use case was cited!

Now we are back on track! I get to go back to my simple route declaration with resource :profile, only: :show and without changing my controller code at all.

But now I needed users to be able to access each others profiles. Again, the Rails documentation had me covered.

Because you might want to use the same controller for a singular route (/account) and a plural route (/accounts/45), singular resources map to plural controllers. So that, for example, resource :photo and resources :photos creates both singular and plural routes that map to the same controller (PhotosController).

And our implementation stays clean.

{% gist e8d6641349e4d0ea7e68d22dd3755e9d %}

This was awesome until I needed to use path helpers. With this implementation, profile_path(:id) works as expected but profile_path does not. If the order is reversed in the routes configuration, profile_path will work and profile_path(:id) will not. This is the result of a bug in the Rails core that touches some pretty intricate code that is linked to other issues. One has even been open for five years!

And we can work around that one as well by overriding the named helpers. Passing as: to our resource definition creates the helpers with a new name.

Our final code is ready!

{% gist e9dcc4cd4bad89554fb01be6627c7b63 %}

In our application, we can reference a generic profile with profile_path(:id) while still having current_profile_path to direct the user to their own profile.