How to Add Dynamic Subdomain in Rails Application

For those who want to implement subdomain programmatically within your rails application, you can follow these few steps:
1. Generate Account Model
First, let’s create an Account model as a table to store subdomain names that we will accept in our application.
rails g model Account subdomain:string
From the above generator, it generates a migration file. We can then do rails db:migrate.
2. Add Request-based Constraints dan Modify routes.rb
Create a new file inside the /lib folder and give it the name, say subdomain.rb.
Add this code inside that file:
class Subdomain
def self.matches?(request)
request.subdomain.present? && request.subdomain != "www"
end
end
Then we need to modify a bit our routes.rb by adding constraints to every URLs that we will use under the subdomain. For an example, pages_controller.rb needs to be accessible under a specific subdomain, so we can write our route like this:
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
constraints(Subdomain) do
get '/', to: 'pages#index'
end
end
3. Add Authorization to Controller
We will then need to add before_action :require_subdomain since the controllers need to be strictly accessible from the subdomain only. This is achievable by modifying our application_controller.rb:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
helper_method :require_subdomain
private
def require_subdomain
not_found unless subdomain_exist?
end
def subdomain_exist?
account = Account.find_by_subdomain(request.subdomain)
!!account
end
def not_found
raise ActionController::RoutingError.new('404')
end
end
In the application_controller.rb above we now have a helper method called require_subdomain, which is reusable to each controller that requires to be authorized by the subdomain.
class PagesController < ApplicationController
before_action :require_subdomain
def index
# controller logic goes here
end
end
4. Try It Out
Let's use rails console to create a dummy for our Account or subdomain.
$ rails c
Running via Spring preloader in process 15151
Loading development environment (Rails 6.0.0)
2.6.5 :001 > Account.new(subdomain: 'test').save
(0.4ms) SET NAMES utf8, @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
(0.3ms) BEGIN
Account Exists (0.7ms) SELECT 1 AS one FROM `accounts` WHERE `accounts`.`subdomain` = BINARY 'test' LIMIT 1
SQL (0.5ms) INSERT INTO `accounts` (`subdomain`, `created_at`, `updated_at`) VALUES ('test', '2017-07-22 14:58:35', '2017-07-22 14:58:35')
(35.5ms) COMMIT
=> true
Open the browser then visit http://test.lvh.me:3000 and also try accessing some random subdomain, for example, http://doesnotexist.lvh.me:3000.
About lvh.me domain, you can check this article out
If the subdomain is registered in Account

Subdomain doesn't exist in Account

Tip:
We can add some extra validation layers in the Account creation so we can preserve some subdomain names so your user cannot claim those (i.e. www, admin, app, etc).
Thank you!
Comments
Join the discussionNo comments yet. Be the first to comment.




