image by Bogdan Karlenko
Use Rails URL helpers outside views and controllers
Occasionally you’ll need to generate a URL to your own application, outside of views or controllers. In these places, URL helpers are available. These helper methods are generated from your routes, e.g. user_books_path(user)
.
Instead of …
…hard coding a URL:
class RequestUserCallBackJob < ApplicationJob
def perform(user)
Net::HTTP.post(
"http://userinfoapi.com/",
body: {callback_to: "https://myapp.com/user/#{user.id}"})
end
end
Use…
…the route helpers that Rails includes automatically in controllers.
class RequestUserCallBackJob < ApplicationJob
include Rails.application.routes.url_helpers
def perform(user)
Net::HTTP.post(
"http://userinfoapi.com/",
body: {callback_to: user_url(user, host: "myapp.com")})
end
end
URL helpers are used in the context of a web request, i.e. within a view or controller. In these cases the host
or domain of the request is provided automatically by the application. Outside of this context you’ll need to ensure you specify a host
for any helpers ending in _url
.
An enhanced version of this pattern is to use an Active Support concern and piggyback on the (probably) already set Action Mailer url options.
module Routing
extend ActiveSupport::Concern
included do
include Rails.application.routes.url_helpers
end
def default_url_options
Rails.application.config.action_mailer.default_url_options
end
end
class RequestUserCallBackJob < ApplicationJob
include Routing
def perform(user)
Net::HTTP.post(
"http://userinfoapi.com/",
body: {callback_to: user_url(user)})
end
enda
Why?
It’s better to use the URL helpers throughout your application since they’re convenient and consistent. Should you change a route for any reason and fail to update the relevant URL method, your tests will alert you to the problem. You wouldn’t get this advance warning if you had interpolated an id
into a hard-coded URL.
As demonstrated in the example above, I typically find I need this pattern when calling out to external APIs that require a webhook to listen for a response.
You might also use this if you’re writing an API for your application that exports a field describing URLs of resources in your application.
{
"id": 23432,
"name": "Nadia",
"links": {
"self": "https://yourapp.com/user/23432.json"
}
}
However, an API response is its own thing. So you should probably use tooling for generatng API responses—such as active_model_serializers
—rather than be mixing URL helpers in yourself.
Why not?
If you’re trying to use route helpers in a place where they aren’t already included, this might be an indication that this is a place you shouldn’t be using them.
Avoid using your routes within an Active Record model, there’s almost certainly a better place!
Last updated on February 22nd, 2021