rails-multi-tenancylisted
Install: claude install-skill mickzijdel/rails-toolkit
# Rails Multi-Tenancy Patterns
## Pattern 1: URL-Based Tenancy with Middleware
Scope requests to tenants without subdomains or separate databases: Rack middleware extracts the tenant ID from the URL path, moves it from `PATH_INFO` to `SCRIPT_NAME` (making Rails think it's "mounted" at that prefix), and sets the tenant context for the request.
```ruby
# config/initializers/tenanting/account_slug.rb
module AccountSlug
PATTERN = /(\d{7,})/
FORMAT = "%07d"
PATH_INFO_MATCH = /\A(\/#{AccountSlug::PATTERN})/
class Extractor
def initialize(app)
@app = app
end
# Account id prefixes in the URL path. Rather than namespace all routes,
# we "mount" the Rails app at this URL prefix.
def call(env)
request = ActionDispatch::Request.new(env)
# $1, $2, $' == script_name, slug, path_info
if request.script_name && request.script_name =~ PATH_INFO_MATCH
# Likely due to restarting the action cable connection after upgrade
env["app.external_account_id"] = AccountSlug.decode($2)
elsif request.path_info =~ PATH_INFO_MATCH
# Yank the prefix off PATH_INFO and move it to SCRIPT_NAME
request.engine_script_name = request.script_name = $1
request.path_info = $'.empty? ? "/" : $'
env["app.external_account_id"] = AccountSlug.decode($2)
end
if env["app.external_account_id"]
account = Account.find_by(external_account_id: env["app.external_account_id"])
Current.with_acc