Rest – Is is OK to use a non-primary key as the id in a rails resource

restroutingruby-on-railssurrogate-keysurl

I am getting ready to set up a resource for some new api calls to my rails application.

I am planning on calling the resource devices ie

resources :devices

This is going to represent android mobile devices

I know this will get me routes such as

GET devices/:id

In most cases :id would be an integer representing the primary key, and in the controller we would use :id as such:

GET devices/1
@device = Device.find(params[:id])

In this case I would like to use :id as the google_cloud_messaging_reg_id

So I would like to have requests like this:

GET devices/some_long_gcm_id

and then in the controller , just us params[:id] to look up the device by the gcm registration id. This seem more natural, since the device will know it's gcm id rather than it's rails integer id. Are there any reasons I should avoid doing this?

———————————————————————

UPDATE:

My initial concern was that seeing a route (via rake routes) of /devices/:id or looking at the controller and seeing params[:id] would be confusing since most would assume :id was the integer primary key.

After thinking about it some more, and reading Idan Arye's answer below, my current thinking is to create routes like shown below. This will get me routes (via rake routes) that look like this:

GET    /api/v1/android_devices/:gcm_reg_key(.:format) 

which I think makes it somewhat clear that :gcm_reg_key may not be a primary key integer.
And in the controller the parameter would be

params[:gcm_reg_key]

Again, I think this makes it somewhat clear that the param may not be an integer primary key (convention would be params[:id])

Any other thoughts or suggestions are welcome.

match '/android_devices/:gcm_reg_key', to: 'android_devices#show' , via: :get, as: 'android_device'
match '/android_devices/:gcm_reg_key', to: 'android_devices#update', via: :post, as: 'android_device_create'
match '/android_devices/:gcm_reg_key', to: 'android_devices#update', via: :patch, as: 'android_device_modify'
match '/android_devices/:gcm_reg_key', to: 'android_devices#destroy', via: :delete, as: 'android_device_destroy'
resources :android_devices , only: [:create, :index]

Best Answer

Actually, there are 3 reasons:

  1. Convention
  2. Convention
  3. Convention

Convention is important, because it makes it easier for other programmers to understand your code. In RoR it's even more important, since many facilities of the framework and 3rd party plugins are assuming you are following the convention. Often they'll offer a configuration interface you can use in case you violate the convention, but this will make their usage more verbose, complicated, and error-prone.

So, you should only break convention if you have a really good reason to do so - and this isn't one, because there is a much simpler alternative: using the GET parameters!

Instead of hijacking the show action, create a new route - e.g. find_and_show. That action will accept the GCM id as a GET parameter instead as part of the path, so the path will be:

GET devices/find_and_show?gcm_id=<some_long_gcm_id>

The method should find the Device by the gcm_id, and the rest of it could be shared with the regular show method, or even delegated to it with a redirect.

This way you are not breaking any convention, you are making it clear that you are searching by GCM id rather than by the PK, and if you ever want to allow retrieving via other unique identifiers you can simply make find_and_show support more GET parameters.

And yes, it'll make the URLs longer, but it's not like humans will type these URLs...

Related Topic