How to create multiple Plug forwards depending on configuration

Hi,

I’m trying to write a simple Plug app that mounts various Phoenix endpoints depending on configuration.

Configuration is a simple hash: %{"/one" => PhoenixOne.Endpoint}

defmodule Bof do
  use Plug.Router

  plug(:match)
  plug(:dispatch)

  for {url, endpoint} <- Application.get_env(:bof, :endpoints) do
    quote do
      forward(unquote(url), to: unquote(endpoint))
    end
  end

  #forward "/dwa", to: NomierDwaWeb.Endpoint

  get("/", do: send_resp(conn, 200, "Welcome"))
  match(_, do: send_resp(conn, 404, "Oops!"))
end

Also tried Enum.map but it won’t work.

Any ideas?

Thanks for help!

1 Like

What doesn’t work for you in this case? Is there a compilation error?

It compiles just fine, just get ‚Oops!’ response.

I think it has something to do with the quote causing the definitions to not be created.

I ran into something similar creating a number-to-words function: https://gist.github.com/mbuhot/ba2232750f8e892c4c52142b556c563a

The trick there was to imperatively invoke def inside Enum.each.

Because it is the Plug router creating functions will no work in this case.

What about Code.eval_quoted? Wouldn’t it just put forwards into the code?

defmodule Bof do
  use Plug.Router

  plug(:match)
  plug(:dispatch)

  for {url, endpoint} <- Application.get_env(:asdf, :endpoints) do
    Code.eval_quoted(
      quote(do: forward(unquote(url), to: unquote(endpoint))),
      [],
      __ENV__
    )
  end

  # forward "/dwa", to: NomierDwaWeb.Endpoint

  get("/", do: send_resp(conn, 200, "Welcome"))
  match(_, do: send_resp(conn, 404, "Oops!"))
end

Feels like a bad idea though.

Tried with

iex(3)> Plug.Test.conn(:get, "/one/api") |> Bof.call([])
[info] GET /one/api
[debug] Processing with AsdfWeb.PageController.index/2
  Parameters: %{"glob" => ["api"]}
  Pipelines: []
[info] Sent 200 in 122µs

# ...

Seems to be working …

2 Likes