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?



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


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]
  • 1
    In database terms, a primary key is the one piece of information that uniquely identifies a particular row in a table. The primary key in a database doesn't have to be an integer. If a GCM registration ID is unique for a device/user and unchangeable, then it can be used as a primary key. – Bart van Ingen Schenau May 30 '14 at 13:08
  • @BartvanIngenSchenau, thanks I was wondering about that, but I was not sure how that would work, since rails normally generates the primary key when the record is created, I do specify that the gcm_id must be unique – nPn May 30 '14 at 16:51
  • @nPn "rails normally generates the primary key when the record is created, I do specify that the gcm_id must be unique" That's overridable. You can define your own primary key, which can be a different type, have a name other than id, etc. ruby-journal.com/… One word of caution: The primary key could be thought of as defining the identity of a record, the way a memory address defines the identity of an object. If you want to be able to change the gcm_reg_key, then you shouldn't use it as a primary key. – Alexander May 4 '20 at 19:27

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...

  • 6
    @IdanArye Thanks for the answer, but I respectfully suggest that this proposal is also a violation of convention. My understanding of RESTful, conventions is that the url should be a “noun”, and the HTTP method (GET, POST, etc.) should be the verb. Introducing the find_and_show segment into the url, brings in verbs and make the url very un-resource like. For example “devices” seems like a resource, while “devices/find_and_show” does not. I think I found a solution I am comfortable with, and will update my question. Again, thanks for the response. – nPn May 30 '14 at 3:43

You have to make a distinction between your database and the url, which is actually a kind of API or front to your data. There is no obligation whatsoever that what is the ID in the url, corresponds to the ID in the database.

Actually, I can give two good reasons for this ID to differ:

  1. A hacker could guess url's of existing other devices, by just incrementing/decrementing the id. Depending on the kind of site you have, this might or might not be relevant.
  2. A url could be more easily remembered if it is on the whole readable/meaningful. Like something like /blogs/:blog_title. And your case applies just perfectly imho.

So personally I have done almost exactly what you said, without changing the routes.

To make sure all link-helpers keep working, you just have to overrule the to_param function for a model (docs).

So in your case

class Device < ActiveRecord::Base

  def to_param


and then you can still write device_path(@device) and it will correctly form the url.

In your controller you just write

def show
  @device = Device.find_by(gcm_reg_key: params[:id])

and then keep your routes as

resources :devices

Use the param option to override the default param name:

resources :devices, param: :gcm_reg_key

That will generate routes like GET /devices/:gcm_reg_key(.:format)

To find a device in your controller:

Device.find_by!(gcm_req_key: params[:gcm_reg_key])

Add this to your Device model to use the gcm_reg_key field in generated urls:

class Device < ApplicationRecord
  def to_param

For example:

device_path(device) #=> /devices/some_long_gcm_id

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.