Consider the following use case: you’d like to run a blogging service on Heroku that allows users to sign up and bring their own custom domain. We’ll call your new idea “Me-dium” (that’s probably not taken).
So you’ve got www.me-dium.com
setup on Heroku. And your users automatically get a subdomain like username.me-dium.com
. But they also want to bring their own domain and CNAME it to username.me-dium.com
.
The Heroku Platform requires that any custom domains be registered with their corresponding app. That way, when a request comes into your custom domain, the Heroku router knows where to send the request. This is a pretty simple process when you have 1 or 2 domains, but when you want to give users the ability to add their own domain, you need a way to tell Heroku about these new domains.
We’ll be adding a simple background worker to interact with the Heroku API. We add a column and form field to the user’s profile for domain
. Then, when they change this value, we update the Heroku API with the new value.
Let’s first start by adding the Heroku API gem to our project.
Next, we need an API token to use with our app.
Add the token to your .env
file:
Let’s set up the worker first. Since this is an external API call, we’ll send this work off to a ActiveJob worker.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
require 'platform-api'
class HerokuDomainJob < ActiveJob::Base
queue_as :default
def perform(domain, action)
heroku = PlatformAPI.connect_oauth(ENV['HEROKU_API_KEY'])
begin
case action
when "add"
heroku.domain.create(MY_HEROKU_APP_NAME, "hostname" => domain)
when "remove"
heroku.domain.delete(MY_HEROKU_APP_NAME, domain)
end
rescue Heroku::API::Errors::RequestFailed => e
Rails.logger.error "[Heroku Domain Worker] ERROR: #{e}"
end
end
end
So this one worker will take two params: the domain in question, and what to do with it (add or remove it). We’ll trigger this worker using an ActiveRecord callback. This method utilizes dirty tracking and we expect a domain
column on the users
table.
1
2
3
4
5
6
7
8
9
10
11
12
13
# app/models/user.rb
after_save :update_heroku_domains, if: "domain.present?"
...
private
def update_heroku_domains
if self.domain_changed? && self.domain_was.present?
HerokuDomainJob.perform_later(self.domain_was, "remove")
end
HerokuDomainJob.perform_later(self.domain, "add")
end
First, we check to see if there was an existing value and that it has been changed (line 9). If so, we send off a worker job to remove the old value (line 10). Then, we add the new value (line 12).
That’s it! Your app is now setup to interact with the Heroku API and can easily add/remove domains as needed. Good luck on your new blogging platform!
Writer. Musician. Adventurer. Nerd.
Purveyor of GIFs and dad jokes.