Hello there! Are you at the point in your Ruby on Rails project where you’d like integer-based slugs like /users/2
to be friendlier and look like /users/matt
instead? How about /posts/how-to-make-great-tacos
instead of /posts/31
? There are many reasons you’d want to do something like this, and there’s a great gem called FriendlyId that can help us out.
This post will focus on how we can use FriendlyId to only return results when we provide the friendly slug. Otherwise, we want to return a 404 to avoid some of the issues below.
What’s wrong with just using the ids Rails gives us?
Nothing, per se, but friendlier slugs give us some advantages that are too good to pass up. Here are some considerations:
- Security – Using simple integers as ids exposes a lot of information you may not want the world to know. It makes it easier for unsavory folks to poke around when they can identify your users by an easy-to-guess number, for example.
- Size – By default, Rails is going to generate the ids sequentially. This means any
Model
you create will be stuck with tiny numbers for a while. Imagine the confidence you’d inspire when you have your client submit payment at/invoice/1
! - Search – Google, Duck Duck Go, etc.- will all appreciate and favor descriptive URLs instead of integer-based alternatives.
/products/mens/jeans/
is a lot more descriptive than/products/2/4
. - Humans – we matter too! It turns out we’re really good at identifying patterns, which makes well-structured URLs helpful to our users. (Despite specific browsers trying to hide that beautiful information)
Installing FriendlyId
I won’t regurgitate their documentation but go to their repo and follow the setup process. Come back here when you’re done.
The Payoff
This is where we get to the whole point of this post. If you’ve read the FriendlyId guide, you know you will be finding database entries using Class.friendly.find
vs. Class.find
. Let’s say you have a controller method that sets the post for several of your actions for a Post
model.
# posts_controller.rb
def set_post
@post = Post.friendly.find params[:id]
end
By default, FriendlyId will allow you to pass either the integer id Rails provides or the fancy new slugs you now have. What you do next will be dependent on where you are in your project. If you have established users who have bookmarks or manually enter specific paths for your website, 404ing the original integer IDs will cause problems. Furthermore, if you have high-ranking pages indexed by Google, then you probably would want to redirect those, at the very least.
However, if you have the good fortune of being early enough in your app that you don’t have the above considerations, you can proceed as follows.
# posts_controller.rb
def set_post
# Using find_by_friendly_id won't look up original integer ids
@post = Post.friendly.find_by_friendly_id params[:id]
# Redirect old slugs using FriendlyId's History module
if params[:id] != @post.slug
redirect_to edit_admin_post_path(@post), status: :moved_permanently
end
end
This setup will process the friendly slugs (even if they are changed at some point) and return a 404 if the integer id is used. I like this process because it solves some of the issues mentioned in this post and is more visually appealing than switching to UUIDs.
Your mileage may vary, but this is an excellent solution if you’re at a point where you can implement it.