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.


Originally published at www.benpickles.com

Like what you read? Give Ben Pickles a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.