Mc Loving gif

Custom & Vanity URLs with Rails

sy avatar

Written by Sy Rashid

Dec 20 · 9 min read

Imagine you’re 40, no need for those IDs at the bar anymore 👴🏻 ![](https://miro.medium.com/max/1000/0*AMTFHT33FNuU5ovv.gif) It all started with a student asking how to create invite links for groups in a final project. Similar to what you’d see in apps like Jamm. ![](https://miro.medium.com/max/1260/1\*MmJvPJAqJORM0oEvDZ931A.png) So, like any good programmer, I answered in an eloquent and efficient manner that perfectly walked through the concept for them 👩‍🏫. …and by that I mean, I started googling frantically how to build something like this. Alas , with very few results, it was time to set out on my own and see what I could come up with. A few hours and a delicious cheese strudel later, here we are. So let’s break down this problem, as there are two parts here. 1. We need custom or vanity urls for our invite links to groups. 2. We need a way to link these urls to create a group membership for users (signed in and signed out) as well as people who don’t have accounts yet. This article will cover part one, for part two you’re on your own ✌️. ![](https://miro.medium.com/max/996/0\*DNQLQTRfWbNgVAHk) Just kidding, it’s linked at the bottom. Anyways, let’s get into building those custom and vanity URLs. **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… ![](https://miro.medium.com/max/1400/1*_GMFT6LhloLB05Uh6u6D9A.png) 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_ 🤮_)_ ![](https://miro.medium.com/max/1000/0\*ZJJwsVgtrWjcTRhL.gif) So just like our slug friend above, let’s get to it. To set our URLs up we’ll follow a 4-step process. 1. Generate a unique and present slug 2. Use that slug as a param in our routes 3. Override the ActiveRecord method #to\_param in your model 4. 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: ![](https://miro.medium.com/max/1400/1\*CLBfqLlskPbvDEKFkzGrQg.png) 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: ![](https://miro.medium.com/max/1400/1\*sBrrQyQezUiSafalCTBR3A.png) 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](https://api.rubyonrails.org/classes/ActiveRecord/SecureToken/ClassMethods.html) 👈🏻 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. ![](https://miro.medium.com/max/1400/1\*-t0xKUEeLBYVjc8nuQcL2w.png) 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 🔍 ![](https://miro.medium.com/max/1400/1\*0AqQxfeUhkUUVkTMumjyww.png) 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](https://guides.rubyonrails.org/routing.html#overriding-named-route-parameters) ### _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: ![](https://miro.medium.com/max/1400/1\*O4QkqJVPOBJjctafy\_zjww.png) By overriding the to\_param method and returning the slug we defined, the routes we generate will change: ![](https://miro.medium.com/max/1400/1\*4dwn3FqAFKpg7Um7tbpTmg.png) Read a bit more here 👉 [#to\_param](https://apidock.com/rails/ActiveRecord/Integration/to_param) ### _Step 4: Look up records using the slug param you defined from before_ Finally, the last step, we made it fam 👨‍👨‍👦 👩‍👩‍👦 👨‍👩‍👧 ![](https://miro.medium.com/max/1000/0\*0Emtu03oTPhBsPJT.gif) 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: ![](https://miro.medium.com/max/1400/1\*mtv9XIbUnfkr8iIXHGnA4w.png) And in any nested resource’s controllers you may see something like this: ![](https://miro.medium.com/max/1400/1\*SrMHrxs6zOWow6ztgWMprg.png) 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](/blog/passing-referral-codes-in-rails)