DEV Community 👩‍💻👨‍💻

Dr Nic Williams
Dr Nic Williams

Posted on

Friendly IDs for Ruby on Rails

Do you have URLs like /books/1 or /secret_things/10 and wish that they were friendlier? Also, wish the IDs were sequential and guessable?

I like Stripe's URLs, such as https://dashboard.stripe.com/test/products/prod_MG5m4q7sKvGto8. If I see the ID value prod_MG5m4q7sKvGto8 anywhere I know it belongs to a Stripe Product. cus_MG5sTiccdSlpjw? A Stripe Customer ID. Very friendly.

And the IDs are not sequential. Friendly and random. I want this.

I'm going to show you the project I use for friendly IDs, and how I add them to my Rails scaffolding generators so they are automatically enabled for every model.

rails generate scaffold Book title description:text
Enter fullscreen mode Exit fullscreen mode

And my URLs automatically pop out looking like /books/book_qYlVPJvDprRabHa0wy1xz3n9.

A quick tweak of the generated class and they might become even nicer, /books/bk_qYlVPJvDprRabHa0wy1xz3n9.

Yeah, you want this too.

prefixed_ids gem

The magic is provided by Chris Oliver's prefixed_ids rubygem.

The only requirement is that your models' id column is bigint or some other integer. I was not successful using the gem with uuid columns. But you won't need UUID IDs because you'll have publicly friendly, random IDs, and internally sequential IDs.

bundle add prefixed_ids
Enter fullscreen mode Exit fullscreen mode

Trying it out

Before we go and edit your scaffold generators, let's try it out on an existing model.

Add has_prefix_id :thing to one of your models, and go and view it in your app.

class Book
  has_prefix_id :bk
Enter fullscreen mode Exit fullscreen mode

Your boring IDs now look gloriously friendly, /books/bk_x912t423.

The gem hijacks the find and to_param methods. Everything just works.

Adding it to your model generator

You definitely want friendly IDs for all your future models. That is, when you run either:

rails g model Book title
rails g scaffold Book title
Enter fullscreen mode Exit fullscreen mode

You want the resulting app/models/book.rb to include the has_prefix_id :bk link from above.

First, copy the current Rails model.rb.tt template into your app.

If you're using Jumpstart Pro, you can skip this step.

mkdir -p lib/templates/active_record/model
curl https://raw.githubusercontent.com/rails/rails/main/activerecord/lib/rails/generators/active_record/model/templates/model.rb.tt \
  -o lib/templates/active_record/model/model.rb.tt
Enter fullscreen mode Exit fullscreen mode

This file will be used to generate all your future model class files.

In the newly created model.rb.tt file, add the 3rd line:

<% module_namespacing do -%>
class <%= class_name %> < <%= parent_class_name.classify %>
  has_prefix_id :<%= singular_name %>
Enter fullscreen mode Exit fullscreen mode

If you were to generate a Book model, it would look like:

class Book
  has_prefix_id :book
end
Enter fullscreen mode Exit fullscreen mode

You can edit :book to :bk. Pick an abbreviation that resonates with your URL-appreciating customers.

Show them you care.

Top comments (2)

Collapse
 
katafrakt profile image
Paweł Świątkowski

I'm not sure if it's friendlier, but certainly interesting and easily hiding the monotonic id from the user. Does it use hashids under the hood?

Collapse
 
eminarium profile image
Merdan Durdyyev

Nice one, Nic.
I loved this. Hope it has some custom configurations like ‘friendly_id’ gem.
Anyway, a simpler and an interesting approach!

Timeless DEV post...

How to write a kickass README

Arguably the single most important piece of documentation for any open source project is the README. A good README not only informs people what the project does and who it is for but also how they use and contribute to it.

If you write a README without sufficient explanation of what your project does or how people can use it then it pretty much defeats the purpose of being open source as other developers are less likely to engage with or contribute towards it.