Conditional forward in Router based on run-time configuration

I need to use the forward macro in my application’s router only if a configuration flag is true, otherwise I don’t need to forward. I’ve set the config flag to my Endpoint and am trying to access it in the router module as such:

if MyApp.Endpoint.config(:use_sent_emails_plug, false) do
  forward "/sent-emails", Bamboo.SentEmailViewerPlug
end

However this approach throws a compilation error:

== Compilation error in file lib/my_app_web/router.ex ==
** (ArgumentError) argument error
    (stdlib) :ets.lookup(MyAppWeb.Endpoint, :use_sent_emails_plug)
    lib/phoenix/endpoint.ex:540: MyAppWeb.Endpoint.config/2
    lib/my_app_web/router.ex:4: (module)
    (stdlib) erl_eval.erl:680: :erl_eval.do_apply/6
    (elixir) lib/kernel/parallel_compiler.ex:229: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/7

Thanks in advance for any help and insight.

@stefpankov The config function you’re calling is designed to allow for dynamic runtime configuration, and errors if you call it at compile time.

If you don’t need to change the value at runtime you should just access the config value directly:

if Application.get_env(:my_app, MyApp.Endpoint)[:use_sent_emails_plug] do
  # etc
end

I’d probably move it out from under Endpoint at that point as well since it isn’t really part of the official endpoint config.

I had the mail adapter be configurable at run-time in the releases.exs config file, however i couldn’t really find a way to use that configuration in the router. After a lot of digging I figured out the whole compile time vs. run-time configuration and realized that there’s really no need for me to configure the mail driver at run-time so now for production I configure the mail driver with an env variable and it can be either local or sendgrid for different target environments.

So now the check looks like this:

if Application.get_env(:my_app, MyApp.Mailer)[:adapter] == Bamboo.LocalAdapter do
  forward "/sent-emails", Bamboo.SentEmailViewerPlug
end
1 Like

This page is the top Google result for “phoenix conditional router forward”, so I’ll just leave a decently thorough answer that will hopefully help whoever bumps into this thread. (I’ll be honest: It’s probably gonna be me in 6 months! :sweat_smile:)


To forward a request to one of several different routers in Phoenix, do this:

  1. Forward the matching path in your router:

lib/your_project_web/router.ex

  scope "/" do
    pipe_through :api

    forward "/", YourProjectWeb.Plugs.ConditionalRequestForwarder
  end
  1. Create a plug that matches and dispatches to the appropriate router based on your condition:

lib/your_project_web/plugs/conditional_request_forwarder.ex

defmodule YourProjectWeb.Plugs.ConditionalRequestForwarder do
  @behaviour Plug

  def init(opts), do: opts

  def call(%Plug.Conn{} = conn, _opts) do
    case conn.params["some_param"] do
      nil -> YourProjectWeb.SomeRouter.call(conn, [])
      _ -> YourProjectWeb.SomeOtherRouter.call(conn, [])
    end
  end
end

And that’s how you can conditionally forward a request to one of several possible routers! :tada:

2 Likes