I’m working on a Phoenix app and I would like to implement the feature that we see on many websites where each user have its own page on a custom subdomain.
Do you need to serve on example.com as well as subdomains? If not, then you can add a plug before the router in endpoint.ex that extracts the subdomain from conn.host and assigns some necessary info to distinguish between users into conn.assigns.
defmodule AppWeb.Plugs.UserFromHost do
@behaviour Plug
import Plug.Conn
@impl true
def init(opts), do: opts
@impl true
def call(conn, _opts) do
case String.split(conn.host, ".") do
[sub, "example", "com"] -> assign_user(conn, sub)
_other -> conn
end
end
defp assign_user(conn, sub) do
assign(conn, :user, ...)
end
end
Then you can replace the plug Router in endpoint with a plug that calls either the main website router or a user router based on conn.host.
lib/app_web/endpoint.ex
# ...
plug AppWeb.Plugs.RouterForwarder
# plug AppWeb.Router <- remove this line
# ...
lib/app_web/plugs/router_forwarder.ex
defmodule AppWeb.Plugs.RouterForwarder do
@behaviour Plug
import Plug.Conn
@impl true
def init(opts), do: opts
@impl true
def call(conn, opts) do
case String.split(conn.host, ".") do
["www", "example", "com"] -> AppWeb.Router.call(conn, opts) # the router from `endpoint.ex` moved here
[sub, "example", "com"] -> conn |> assign_user(sub) |> AppWeb.UserRouter.call(opts)
["example", "com"] -> AppWeb.Router.call(conn, opts) # and here
end
end
defp assign_user(conn, sub) do
assign(conn, :user, ...)
end
end
In my experience this does “lose” the subdomain on navigation, and requires lots of repetition threading the path param into every link helper. May be ways to reconcile that, tho
Hi, it is possible to use the phoenix router scope, to dispatch by request host. I haven’t used it yet and don’t know, if the routes.Helper supports this, but see: https://hexdocs.pm/phoenix/Phoenix.Router.html#scope/2