Hello,
I’m working on a Plug application and I’m trying to find an easy way to “mount” it into a Phoenix app or another Plug app.
My Plug application is built on Plug.Router
, and responds to some GET requests to render HTML and POST/PATCH/DELETE from the clients. It’s meant to be a web dashboard and control panel for another package I’ve been working on.
I can successfully run it standalone with Plug.Adapters.Cowboy.http(...)
, but I would like to make it embeddable into a host application. The use case is that you’d use the main package for its functionality, and the web control panel would be an optional extra that you can serve from the application itself. This practice is common in Ruby on Rails, for example.
An additional requirement is that it should be possible to mount it at an arbitrary path. My app needs to be aware of this path, or namespace, because it needs it to build the relative paths in its HTML pages (e.g. <form action="/custom-namespace/foo">
).
Now, I’m having some troubles finding a good way to integrate it.
I’ve found this other package that does something similar (I guess that’s a typo and they actually meant “Using with Phoenix”), but the API seems clunky:
# phoenix router
pipeline :exq do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_flash
plug :put_secure_browser_headers
plug ExqUi.RouterPlug, namespace: "exq"
end
scope "/exq", ExqUi do
pipe_through :exq
forward "/", RouterPlug.Router, :index
end
To be honest I don’t like the repetition, and I’m wondering if it’s really necessary, but I’ve tried this approach nonetheless.
In that example, ExqUi.RouterPlug
will use the KW option to set that namespace in conn.assigns
, which will then be used by the Router
. Interestingly, the plug will invoke the router even though the forward
will basically do the same thing a couple of lines later. This works because all the routes in that Plug router end with halt()
, which will make the forward
pointless or, rather, it’s there just to make the Phoenix router’s pattern matching work.
I’ve tried to adapt my own Plug router (or use a separate plug) so that I could plug
it directly into the pipeline, but if I don’t declare anything in scope
it will complain because there are no routes matching the request (which makes sense, I guess).
If I add the forward
statement a few lines later in my scope
and add halt()
in all the routes in my plug router, it works. As I said, however, I don’t really like the repetition.
While I would appreciate comments on whether there is a more elegant variation on that aproach, I can think of two other ways to do what I want.
The first one is to configure the path namespace in the Mix config file. It would work, but I’d have to duplicate that info in two places.
The alternative is that I could just forward
to the router, and then extract the first path fragment and set it in conn.assigns
for later use.
Is there a better and established way?