Dynamically setting Rails default_url_options
in Heroku review apps
I love Heroku review apps but not when URLs in emails point to the parent app — and if you have a hypermedia API its _links
can end up totally broken. Why does this happen and how can it be fixed?
My usual approach used to be to configure Rails.application.routes.default_url_options[:host]
from a DEFAULT_URL_HOST
environment variable, but if the review app inherits this variable then all of its URLs generated outside the controller/view request/response cycle — including URLs in emails and serializers such as ActiveModel::Serializers — incorrectly point to the parent app. In the past I’ve been forced to manually update a review app’s config but that isn’t a solution.
It isn’t possible to dynamically set an environment variable but you can detect a review app at run-time by enabling the HEROKU_APP_NAME
environment variable and then use it to generate the correct host. To do this in Rails create an initializer config/initializers/default_url_options.rb
with the following contents:
# If a default host is specifically defined then it's used otherwise the app is
# assumed to be a Heroku review app. Note that `Hash#fetch` is used defensively
# so the app will blow up at boot-time if both `DEFAULT_URL_HOST` and
# `HEROKU_APP_NAME` aren't defined.
host = ENV['DEFAULT_URL_HOST'] ||
"#{ENV.fetch('HEROKU_APP_NAME')}.herokuapp.com"
# Set the correct protocol as SSL isn't configured in development or test.
protocol = Rails.application.config.force_ssl ? 'https' : 'http'
Rails.application.routes.default_url_options.merge!(
host: host,
protocol: protocol,
)
In staging and production apps you should set the DEFAULT_URL_HOST
using heroku config:set
, but in review apps tell Heroku to pick up the HEROKU_APP_NAME
variable by adding it to the app’s app.json
(and remove references to DEFAULT_URL_HOST
):
{
"env": {
"HEROKU_APP_NAME": {
"required": true
}
}
}
Note that this also means in development/test/CI you’ll need to define the DEFAULT_URL_HOST
environment variable or the app won’t boot — on my development machine I use direnv, dotenv is also popular.
And that’s it, now all URLs generated in emails and serializers will correctly point to the review app’s host.
Also on medium.com.