Custom & Vanity URLs with Rails
By: Sy Rashid
Tags: Ruby, Software Design, Software Development, Rails, Web Development

The Theory

When we normally play with resources in Rails, we’re given something we often take for granted, those nifty id’s generated by our DBs. Not only are these unique per instance, but their presence is guaranteed.

Now while you may be saying “No duh! Of course id’s generated by a db are going to be unique, as well as present,” it’s important to mention, as this same principle will apply to whatever we decide to replace those id’s with, be they usernames as you see on github…

Or as we saw above with Jamm where they generated an obscure url per group with a token to prevent people from guessing an id number and getting the invitation.

These parts of the URL, which identify a particular page (or resource) on a website are known as slugs. (…gross I know 🤮)

So just like our slug friend above, let’s get to it. To set our URLs up we’ll follow a 4-step process.

Generate a unique and present slug Use that slug as a param in our routes Override the ActiveRecord method #to_param in your model Look up records using the slug param you defined from before

Step 1: Generate a unique and present slug

Two approaches to take here. The first is if you are writing a vanity url, for example with a username. In this case, simply add that attribute to your model through a migration or define it during setup, and then add validations to validate presence and uniqueness as below:

The validation is the part you want to focus on, the next article will discuss the has_many relations

The other approach is to generate url’s using long sequences of unique characters, which I’ll refer to as tokens from here on out, as we saw with Jamm.

For this approach, I wanted to generate a unique token for each group to be used as the slug . Fortunately, to save us from adding another gem, Rails 5 introduced secure_token that will take care of generating 24-character unique tokens with minimal setup as can be been below #blessed 🙏:

The first step here is to generate an attribute on our model we want to create these vanity urls for. In our case, for Group, we can do this in two ways, either when we generate the model, or if it already exists, then generate a migration to add the attribute. In either case we’ll need to pass the argument token_name:token to the generator. Rails will recognize this and set up the correct attribute in our model.

From there we need to tinker with the model itself as below:

By applying this has_secure_token method with an attribute of my choice my Group model now generates instances of Groups with that 24-character unique token upon save or create.

And if I ever needed to regenerate my token, all I’d need to apply is the method #regenerate_invite_token to the instance in question.

See more below in the documentation 👇

Delightful, we’ve got our slug to use, now it’s on to step 2!

Step 2: Use that slug as a param in our routes

By default, Rails will use the :id as the default resource identifier. For us to override that, we simply pass a param option with the attribute we’d like to use in it’s place. In our case of invite links we want to pass the :invite_token we created in step 1.

Again don’t worry about the membership or invite_link stuff, we only care about the param: :invite_token

Now when we run rails route’s we’ll start to see things like this 🔍

Instead of seeing the traditional :group_id or :id from before, we now see :group_invite_token and :invite_token in their place.

Read more here in the Rails Documentation

Step 3: Override the ActiveRecord method #to_param in your model

Now this is all great, but we’ll still run into a hiccup. If we pass an instance of our model when we try and build a URL, for example with a link_to, the URL will still be generated with the models :id and NOT the :invite_token. 😭

This behavior is defined in the #to_param method in ActiveRecord. Fortunately this behavior is easy to override in your model:

By overriding the to_param method and returning the slug we defined, the routes we generate will change:

Read a bit more here 👉 #to_param

Step 4: Look up records using the slug param you defined from before

Finally, the last step, we made it fam 👨‍👨‍👦 👩‍👩‍👦 👨‍👩‍👧

In our controllers, we’ll often times need to look up and find the resource in question. Minor change here, instead of looking and finding our Model by :ids we’ll instead be finding them through the slug we defined. In our group controller you may see something like this:

And in any nested resource’s controllers you may see something like this:

And voila! 🎉

We are done, you are now creating custom URLs for your Rails application instead of the boring ones from before!

In our example we proceeded with setting up a custom URL for a :invite_token attribute, if you’re in the market for more of a vanity URL such as with :username, just replace :invite_token with :username or whatever attribute you’d like in the example above.

For part two, of this little miniseries please check out the article below. We’ll be using these custom URLs we’ve just set up to create a group memberships for users (signed in and signed out) as well as people who don’t have accounts yet. 👇

Passing Referral Codes or Invite Links w/ Devise in Rails

— — — — —

These are hilarious to me, watching someone write about themselves in the third person.

Sy Rashid is a former child who used to hop on the occasional flight and works as a designer and developer at his web development agency MANGOTREE. When he’s not busy coding he teaches as a Lead Instructor for Le Wagon — #1 coding school globally — where he has trained 100+ students across 3 campuses to bring over 25+ product visions to life thru teaching, guidance, and product management.

Curious to see the shenanigans he’s been getting himself into? Connect with him on Linkedin or MANGOTREE to grab a casual coffee or an extremely formal taco.