Duplicate scope entries with different pipe_through?

Hey all! Just ran into a small considering that I don’t have a good answer for.

I’m building an application that uses Absinthe and I’d like to expose its GraphiQL interface. One fun aspect of this is that any external traffic to this application’s admin/ routes is denied by default unless you’re on VPN. Because of this I want to expose both the Phoenix dashboard under the /admin scope with…

scope "/admin" do
  import Phoenix.LiveDashboard.Router
  pipe_through [:fetch_session, :protect_from_forgery]

  live_dashboard "/dashboard", metrics: MyApplicationWeb.Telemetry
end

scope "/admin" do
  pipe_through [:api]

  forward "/graphiql", Absinthe.Plug.GraphiQL,
    schema: MyApplicationWeb.Schema,
    interface: :playground,
    context: %{pubsub: MyApplicationWeb.Endpoint}
end  

An issue I’m seeing with this is that I’m duplicating scopes here and don’t understand enough about the Phoenix Router to know if this the right way to expose two different endpoints that require different pipelines under the same /admin scope. I’ve gone ahead and tested out keeping them under the same scope but ran into some funky behavior where depending on how I order things I’ll get broken behavior.

For example, this seems to work:

scope "/admin" do
  pipe_through [:api]
  forward "/graphiql", Absinthe.Plug.GraphiQL,
    schema: MyApplicationWeb.Schema,
    interface: :playground,
    context: %{pubsub: MyApplicationWeb.Endpoint}

  import Phoenix.LiveDashboard.Router
  pipe_through [:fetch_session, :protect_from_forgery]
  live_dashboard "/dashboard", metrics: MyApplicationWeb.Telemetry
end

Whereas this does not work:

scope "/admin" do
  import Phoenix.LiveDashboard.Router
  pipe_through [:fetch_session, :protect_from_forgery]
  live_dashboard "/dashboard", metrics: MyApplicationWeb.Telemetry

  pipe_through [:api]
  forward "/graphiql", Absinthe.Plug.GraphiQL,
    schema: MyApplicationWeb.Schema,
    interface: :playground,
    context: %{pubsub: MyApplicationWeb.Endpoint}
end

Without knowing much more about how this works “under the hood”, I’m really just guessing at which will cause fewer problems in the long run. Curious if people have dealt with this situation before and how you think about :slight_smile:

1 Like

scope is fine to repeat - all it does is push a set of options onto the router’s stack:

The ordering issue you’re seeing is because pipe_through adds the pipes to the scope, but they only apply to calls to route and scope following them in the scope. For instance, here’s where Scope.push grabs the current value of pipes:

As a result, the GraphiQL forward will see just the api pipe in the first ordering, and fetch_session, protect_from_forgery, api in the second.

IMO the clearest way to avoid needing to remember exactly how this ordering thing works is:

  • keep pipe_through calls together at the top of a scope
  • different pipelines? different scope.
1 Like

@al2o3cr thanks for taking the time to respond! The explanation makes sense and thanks for sharing your views on the source. Definitely helpful for building out that mental model that I was missing :slight_smile:

1 Like