Strategy for multiple subdomains --> scoped queries?

A “single tenant” pattern is best for my app: publishing laws for different states and countries. Most of the time, the queries will be scoped by region, but sometimes not.

I’ll have subdomains like:

  • nevada.public.law
  • oregon.public.law

etc.

By default, an Oregon user should see only Oregon results. But some lawyers need to search across many jurisdictions.

(I have an idea how I’d implement this in Rails: A function that returns the current subdomain, and a scope that’d restrict results.)

What would a Phoenix pattern for this look like? What pieces would I need? Would the sharding support be helpful? Umbrella? Or are those too heavyweight? (E.g. I believe I’ll have all “laws” in one table.)

Thanks!

1 Like

I think that you may be reaching for an overly complex solution. From your problem description I believe that this can be handled with a handful of normal modules and functions.

I believe that given a %Plug.Conn{} you can retrieve the subdomain. So the first function current_juristiction/1 would take a %Plug.Conn{} returns the juristiction (e.g. 'oregon' or 'washington').

Then in your Law (or similar) Context you’d have two functions Law.search_laws/1 and Law.search_laws/2. Law.search_laws/1 would take the query and search all juristictions while Law.search_laws/2 would take the query and the juristiction and search in only that juristiction. The controller would receive a parameter that would tell it to search either only the current juristiction or all juristictions.

2 Likes

I would first use a custom Plug to capture the subdomain. To do this you can add the following file:

defmodule AppName.Plugs.Subdomain do
  import Plug.Conn

  @doc false
  def init(default), do: default

  @doc false
  def call(conn, _) do
    case get_subdomain(conn.host) do
      subdomain when byte_size(subdomain) > 0 ->
        conn
        |> assign(:subdomain, subdomain)
      _ -> conn
    end
  end

  defp get_subdomain(host) do
    root_host = AppNameWeb.Endpoint.config(:url)[:host]
    String.replace(host, ~r/.?#{root_host}/, "")
  end
end

Then you can include include plug AppName.Plugs.Subdomain in your router browser pipeline. You’ll then be able to access the current subdomain via conn.assigns[:subdomain] on every request.

From there you can use the different function like @axelson mentioned as needed and put in place the proper authorizations.

Or, you could have two functions with Law.search_laws/2.

def Law.search_laws("all", query) do
# query all laws
end
def Law.search_laws(jurisdiction, query) do
# query scoped laws for jurisdiction
end
6 Likes

Thank you, both of you - these are great suggestions for me to follow up on.

1 Like