Named Routes

So far, we have seen only one responsibility of the router: to match an incoming HTTP request to a route defined in the routes.rb file and route it to the piece of code that handles it, which is a corresponding action method on a controller class.

Rails Router: From URL to Controller Action

However, the Rails router also works in the other way. That is, given the controller and action, it can generate the URL for that route.

Rails Router: From Controller Action to URL

In addition, you can also name a route and use it to generate the matching URL. Given the route name (along with the corresponding data or model), the router can generate the resulting URL.

But, why do you need Named Routes?

Consider the following route.

get 'post/:id', to: 'posts#show'

When you make a request to post/10, the router invokes the show action on the PostsController class.

What if you want to link to a post? How would you go about creating the URL?

Well, the simplest solution is to interpolate it, passing the post id.

link_to post.title, "/post/#{post.id}"

But hard-coding URLs into your application is not always a good idea. When your URL changes, you'll have to search the whole codebase to find and replace the old URLs. If you miss one, your code won't complain until the users click the link, only to be shown an error page.

To fix this, you could provide the controller and action name along with the post id, so Rails could infer the URL.

link_to post.title, controller: 'posts', action: 'show', id: post.id

However, there's an even better way with the named routes, where you can name a route and use that name to generate the URL.

How to Name a Route in Rails?

You can name a route by passing the :as option. Naming a route creates two methods in your Rails application. The method names follow the convention of {name}_path and {name}_url, where name is the name of the route.

For example, consider the following route.

get "/posts/:id", to: "posts#show", as: "article"

When you name a route, Rails will automatically generate helper methods to generate URLs for that route. These methods are called name_url or name_path, where name is the value you gave to the :as option. For the above route, the router will automatically generate the methods article_path and article_url.

The difference between the two methods is that article_path returns only the path portion of the URL, whereas article_url returns the complete URL, which includes the protocol and domain.

article_path(4) -> `/posts/4`
article_url(4)  -> `https://blog.com/posts/4` or `https://localhost:3000/posts/4`

Once you have named a route, you can use these helper methods in any place where you need a URL or a path, for example, the helpers such as link_to or button_to:

link_to post.title, article_path(id: post.id)

It's less code to write, and it also avoids the problem of hard-coding the URL.

In fact, we can skip the id key, and just pass its value.

link_to post.title, article_path(post.id)

We can go even further. Rails allows you to pass any object to the named helper methods as long as those objects have a to_param method on it.

class Question
  def to_param
    '100'
  end
end

article_path(Question.new) # '/post/100'

What's more, all the active record objects have a to_param method out of box, which returns the object's id, allowing you to do this:

link_to post.title, article_path(post)

This version is more readable, concise, and also avoids the problem of hard-coded URLs.

Pretty cool, right?

How to Access URL Helper Methods in Rails?

You can use the helper methods directly in following places:

button_to user_account_path
class PagesController < ApplicationController
  def profile
    user_account_url
    # ...
  end
end
require "test_helper"

class BookNotesControllerTest < ActionDispatch::IntegrationTest
  test "should get index" do
    get book_notes_url
    assert_response :success
  end
end

Additionally, you can access the helper methods in the Rails console using the app object available in the console.

>> app.root_path
"/"

If the route expects any dynamic segments, you have to pass them via a hash to the named methods. Consider the following route:

get "/portal/:product", to: "pages#portal", as: "portal"

To generate the URL corresponding to this route, you'll pass the product value as follows:

portal_url(product: "ipad")    # "http://www.example.com/portal/ipad", 
portal_path(product: "iphone") # "/portal/iphone", 

In the next chapter, we'll learn how you can override the named route parameter to another name.


If you have any questions or feedback, didn't understand something, or noticed a mistake, please leave a comment below or send me an email. You can also subscribe to my blog to receive future posts via email.