Customized URLs (in Rails) and how to remove IDs from paths
If we look at this same post’s URL, we observe that the path is composed of a long string containing a slugified* version of this post title followed by a random sequence of numbers and letters.
https://medium.com/@a.carreras.c/customized-rls-in-rails-for-dummies-5d0676776577
For Ruby on Rails beginners, most of our Rails apps will work with a pretty basic database. Also, the apps routes will use the index to find for entries in our database. If Medium used that system, this post URL could look something like this: https://medium.com/@a.carreras.c/12
; 12
being the id of this post in the database. In that scenario, it will be pretty likely to have a posts between 1
and 12
, and we could access them just changing the path (for instance https://medium.com/@a.carreras.c/9)
Customizing our URLs will make them not only prettier but also more human-readable - slugified URLs are more sensical, we can guess the page content based on the URL- and more search-engine-indexing-algorithm readable. The latter is also very important because search engine spider crawls will traverse a website through its links storing each page’s data in a database. Part of the stored data is the webpage’s URL, information that will be used for identifying attributes of a webpage and indexing it.
At the same time, using numeric sequential URLs can facilitate scrapping websites. Let’s take a look at the following website:
As we observe, BoardGameGeek uses its board game database index in each game URL. We could easily use Nokigiri to scrap all BoardGameGeek’s games from 1 to … a few hundred thousands.
We can optimize our URLs with two simple steps: installing FriendlyId in your app and slugifying the URL and changing the paths on our Rails app routes.
These following steps are based on a basic blog post model example. You can find the Rails app repository here.
Installing FriendlyId in your app
These are the basic steps to install FriendlyId and use its basic functionality. All of these steps can be found on the FriendlyId’s GitHub page.
- Gemfile. Add
gem ‘friendly_id’, ‘~> 5.1.0’
in your Gemfile and runbundle install
to install it. - Configuration. Run the following code on your terminal
rails generate friendly_id
so that Rails can generate all the FriendlyId migration files. In yourdb/migrate
folder, there will be a new migrationCreateFriendlyIdSlugs
. Runrake db:migrate
to create the new table and add it to your database schema. - IMPORTANT: The models which will be using FriendlyId need to have a
slug
column in their database.
If you are creating a new model, addslug:string:uniq
to it. For example:rails g model Post title:string description:text slug:string:uniq
If you are building up on a previous model, run the following code on the terminal to add a column to your existing tableposts
.rails g migration add_slug_to_posts slug:string:uniq
Runrake db:migrate
to execute the migration. In yourdb/migrate
folder, there will be a new migrationAddSlugToPosts
. - Edit the model. Go to
app/models/post.rb
and add the following code.
class User < ApplicationRecord
extend FriendlyId
friendly_id :title, use: :slugged
end
- Edit the controller. Go to
app/controllers/posts_controller.rb
and replacePost.find
byPost.friendly.find
. For example:
class UserController < ApplicationController
def show
@post = Post.friendly.find(params[:id])
end
endTo scrap
Our app shows the slugged title in the URL. However, if we enter http://localhost:3000/posts/1
, the app will still display the post view. Both id
and slug
work.
In order to prevent that, and only be able to access the post using the slug
, we have to update the app routes to use the slugs instead of the ids.
Removing IDs from paths
- Update your routes to use slugs. Run
rake routes
in your terminal. You will see something like this:
Rails default for routes will use the :id
and redirect to /posts/:id
when using post_path
or @post
.
Go to config/routes.rb
and add param: :slug
to your resources. Also, change :id
by :slug
in any other manual routes. It should look something like this:
Rails.application.routes.draw do
resources :posts, param: :slug
end
Now, run rake routes
in your terminal again. Your routes are redirecting to slug
now.
- Edit the controller. Go to
app/controllers/posts_controller.rb
and replacePost.find(params[:id])
byPost.friendly.find_by_slug(params[:slug])
. For example:
class PostsController < ApplicationController
def show
@user = User.friendly.find_by_slug(params[:slug])
end
end
Updating Slugs with Title Changes
Our app works as we expected. However, our app has a fully CRUD interface implemented. If we update one of our posts, we want to see that change on our customized URL as well.
FriendlyId can do that for us, but we have to empty the attribute slug before it can overwrite it. One option to achieve that, go to app/controllers/posts_controller.rb
and add the following code to the #update
method after a truthy validation: @post.slug = nil if @post.title != params[:title]
@post.update(posts_params(:title, :description))
Check the following video to see the results of our post app with customized URLs.