Overriding Named Route Parameters in Rails

Rails will use :id as the default parameter in resourceful routes.

resources :products

This gives us the following routes:

Prefix  Verb    URI Pattern                   Controller#Action
    products  GET     /products(.:format)           products#index
              POST    /products(.:format)           products#create
 new_product  GET     /products/new(.:format)       products#new
edit_product  GET     /products/:id/edit(.:format)  products#edit
     product  GET     /products/:id(.:format)       products#show
              PATCH   /products/:id(.:format)       products#update
              PUT     /products/:id(.:format)       products#update
              DELETE  /products/:id(.:format)       products#destroy

But what if we aren’t looking up products by :id? We might want to look up products using a SEO-friendly permalink, or perhaps by using some token. You can obviously still pass that value as the id, but this can easily lead to confusion.

A better way is to specify the parameter.

resources :products, param: :permalink

This gives us the following routes:

Prefix  Verb    URI Pattern                          Controller#Action
    products  GET     /products(.:format)                  products#index
              POST    /products(.:format)                  products#create
 new_product  GET     /products/new(.:format)              products#new
edit_product  GET     /products/:permalink/edit(.:format)  products#edit
     product  GET     /products/:permalink(.:format)       products#show
              PATCH   /products/:permalink(.:format)       products#update
              PUT     /products/:permalink(.:format)       products#update
              DELETE  /products/:permalink(.:format)       products#destroy

The only sucky part is that Rails won’t automatically do the right thing when using link_to. So doing link_to @product will still generate a link using the product’s id. If you want to change that you can override the to_param method on your model.

class Product < ActiveRecord::Base
  def to_param
    permalink
  end
end

Happy coding.