Using Subdomains in Rails: Development vs Production
Rails Designer

Rails Designer @railsdesigner

About: I help teams around the world make their Rails apps a bit nicer to maintain and look at. 🎨 Also kickstart SaaS' in a month. 👷 And built a Rails UI Components library, used by 1000+ developers. 🚀

Location:
Amsterdam, The Netherlands
Joined:
Sep 29, 2023

Using Subdomains in Rails: Development vs Production

Publish Date: Aug 6
1 0

This article was originally published on Rails Designer's build a SaaS series


For a current SaaS I am working on I needed to set up a webhook endpoint. I want the production environment to use a subdomain like webhooks.helptail.com, but during development, this needs to work on localhost:3000. So I used some of Rails' routing features for a solution that adapts based on the environment.

The Challenge with Subdomains Locally

When developing locally, Rails applications typically run on localhost:3000, which doesn't support subdomains in the same way as (development) domain name, like a .dev TLD.

To solve this problem, a different routing strategy is needed for development versus production environments.

Here's a clean solution that handles both scenarios elegantly:

# config/routes.rb
constraints Rails.env.production? ? {subdomain: "webhooks"} : {} do
  scope Rails.env.production? ? {} : {path: "webhooks"} do
    post "/:path", to: "workflow/webhooks#create", as: :webhooks
  end
end
Enter fullscreen mode Exit fullscreen mode

This code block does two things:

  1. Production: routes requests to the webhooks subdomain; example: https://webhooks.helptail.com/a-unique-path
  2. Development/test: adds a /webhooks prefix to the path; example: http://localhost:3000/webhooks/a-unique-path

The “magic” happens through the conditional use of Rails' constraints and scope methods. The constraints method applies the subdomain restriction only in production, while the scope method adds the path prefix only in development.

Using separate subdomains for APIs and webhooks (like webhooks.helptail.com) creates a foundation for growth. This approach lets you scale high-traffic parts of your app independently when customer usage surges, implement targeted security policies without complicating your main application (eg. dashboard.helptail.com), and establishes an architecture that simplifies future migrations as you expand (globally through CDNs).

This same pattern works great for API endpoints too. Many applications use an api subdomain:

constraints Rails.env.production? ? {subdomain: "api"} : {} do
  scope Rails.env.production? ? {} : {path: "api"}, defaults: {format: :json} do
    namespace :v1 do
      resources :users
      resources :posts
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

This creates endpoints like:

  • Production: https://api.example.com/v1/users
  • Development: http://localhost:3000/api/v1/users

What do you think of this solution?

Comments 0 total

    Add comment