How to Exclude ID Lookup by Integer When Using FriendlyId

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:

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.

Written by Matt Haliski
Consumer of tacos