Rails 7.1 adds path_params option to url_for for flexible URL generation

railsJanuary 09, 2024Dotby Alkesh Ghorpade

The url_for method generates complete URLs (including protocol, host, port, and path) within Rails applications.

The url_for syntax is as follows. You can pass controller, action, and id as options for this method.

url_for(options = {}, path_params = {}, format: nil)

Before Rails 7.1

Rails scoped routes allow you to group related routes under a common path prefix, controller namespace, or route helper prefix. This helps organize your routes, making them cleaner and easier to understand.

Prior to Rails 7.1, generating links for scoped routes—those nested under a parent resource like account_id required repeatedly specifying the parent ID throughout your views. This often led to tedious code and potential maintenance headaches.

# routes.rb

Rails.application.routes.draw do
  scope ":account_id" do
    get "dashboard" => "dashboard#index"
    get "orders" => "orders#index"

  get "/merchants" => "merchants#index"

In the view file, you must explicitly pass the account_id to the URL helper methods.

<!-- app/views/home/index.html.erb -->

<%= link_to "Dashboard", dashboard_path(account_id: @current_account.id) %>
<%= link_to "Orders", orders_path(account_id: @current_account.id) %>

To resolve the above issue, Rails added the default_url_options method. The default_url_options provides a way to set default options for generating URLs with URL helpers like url_for and link_to. This is particularly useful for ensuring consistent URLs across different environments (development, testing, production) and handling scenarios like email links where you need to specify a different host or protocol.

You need to add the default_url_options method in the ApplicationController and set the account_id as shown below:

# application_controller.rb
class ApplicationController < ActionController::Base
  def default_url_options
    { account_id: current_account&.id }

While the initial approach aimed to associate routes with a specific account through the account_id scope, an unintended consequence emerged: the query param ?account_id=<account_id> was inadvertently appended to all routes, even those outside the intended scope. This led to URLs like:

=> /<account_id>/orders

=> /merchants?account_id=<account_id>

This approach results in aesthetically unappealing URLs, and can lead to caching complications with CDN when employed in real-world scenarios.

In Rails 7.1

Rails 7.1 adds a path_params option to the url_for method. The path_params option ensures precise control over URL path segments. It only incorporates parameters that directly align with named segments in the route, discarding those that don't match, preventing them from becoming query parameters.

You need to change the default_url_options methods as follows:

class ApplicationController < ActionController::Base
  def default_url_options
    { path_params: { account_id: current_account&.id } }

The output of the url_for method considers account_id only when it is required.

=> /<account_id>/dashboard

dashboard_path(account_id: "account-id") 
=> /account-id/dashboard

=> /merchants

merchants_path(account_id: "test-id")
=> /merchants

The benefits of this feature are:

  • Cleaner URLs with fewer query parameters

  • More control over URL structure

  • Potential SEO benefits

To know more about this feature, please refer to this PR.

Closing Remark

Could your team use some help with topics like this and others covered by ShakaCode's blog and open source? We specialize in optimizing Rails applications, especially those with advanced JavaScript frontends, like React. We can also help you optimize your CI processes with lower costs and faster, more reliable tests. Scraping web data and lowering infrastructure costs are two other areas of specialization. Feel free to reach out to ShakaCode's CEO, Justin Gordon, at justin@shakacode.com or schedule an appointment to discuss how ShakaCode can help your project!
Are you looking for a software development partner who can
develop modern, high-performance web apps and sites?
See what we've doneArrow right