Simplest alternative IDs with Rails

Rails actions default on using record IDs. But what if we want to change the URL to something prettier, something that doesn’t leak the record ID in the database?

Luckily, there is a simple answer that doesn’t require you to change much. Let’s say we want to use a slug in the URL for a Team model.

We start with the routes and the param option:

# config/routes.rb
Rails.application.routes.draw do
  ...
  resources :teams, param: :slug do
    member do
      get "another_route"
    end
  end

The param option will change the paths from /teams/:id to /teams/:slug and allow us to use params[:slug] in the controller. All paths generated with resources will be changed.

At the controller level, we now have to look up records with this new alternative ID:

class TeamsController < ApplicationController
  before_action :set_team

  ...

  private

  def set_team
    # Instead of @team = Team.find!(params[:id])
    @team = Team.find_by!(slug: params[:slug])
  end
end

Now things work, but we would need to change all _paths and _url references to point to the new routes. However, these path helpers depend on Model#to_param method which we can override with the new default:

class Team < ActiveRecord::Base
  ...

  def to_param
    slug
  end
end

That’s it. Almost no work and we fixed our IDs-leaking URLs to something better. Don’t forget on the slug index for a quick lookup.

← IT'S OUT NOW

I wrote a complete guide on web application deployment. Ruby with Puma, Python with Gunicorn, NGINX, PostgreSQL, Redis, networking, processes, systemd, backups, and all your usual suspects.

More →