Search code examples
elixirplug

Forward options from a Plug.Router to another Plug.Router


Background

I have a Plug.Router application that receives some options. I need to pass these options to other plugs via forward but I don't know how to do it.

Code

This is the main router. It receives requests and decides where to forward them.

defmodule MyApp.Web.Router do
  use Plug.Router

  plug(:match)
  plug(:dispatch)

  #Here I check that I get options! 
  def init(father_opts), do: IO.puts("#{__MODULE__} => #{inspect father_opts}")

  forward "/api/v1", to: MyApp.Web.Route.API.V1, init_opts: father_opts??
end

As you can probably guess, this won't work. I want my forward call to access the father_opts this router is receiving, but I can't access them.

At first I though about the following code snippet:

def init(opts), do: opts

def call(conn, father_opts) do
  forward "/api/v1", to: MyApp.Web.Route.API.V1, init_opts: father_opts
end

But this doesn't work because I can't put a forward inside a call.

So how do I achieve my objective using forward?


Solution

  • There is an option adding a top-level plug that will store the father options on private and you can fetch that on children call.

    Something like:

    defmodule Example.Router do
      def init(opts), do: opts
      def call(conn, options) do
        Example.RouterMatch.call(Plug.Conn.put_private(conn, :father_options, options), Example.RouterMatch.init(options))
      end
    end
    
    defmodule Example.RouterMatch do
      use Plug.Router
    
      plug :match
      plug :dispatch
    
      forward "/check", to: Example.Route.Check
      forward "/dispatch", to: Example.Plug.Dispatch
    end
    

    Then you can fetch options on the conn in Example.Route.Check.call/2.