Programmatic access to Phoenix Routes

Is it possible to have programmatic access to Phoenix Routes? We are using an instance of a Phoenix.Router to generate in-app links (think myapp://users/42) and I would like to create an admin page that’ll show all the available routes (and generate them with the given ids) but I don’t see any way to introspect the routes. Is it possible? It looks like I could poke in MyApp.AppLinkRouter.__routes__ but that relies on Phoenix internals, so I’d prefer to use a different method.

1 Like

I checked to see what mix phx.routes uses and it uses __routes__, so I suspect that’s the only option.

2 Likes

There is no public API for this today, but I can see how it could be valuable. In this specific case, it sounds like it would be better to use the existing route helpers given that you have a list of resources or ids? For example:

<%= for user <- @users do %>
  <%= link "user #{user.name}", to: Routes.user_path(@conn, :show, user) %>
<% end %>

Is the discoverability of routes necessary in this case, specifically since you have “(and generate them with the given ids” as part of the requirements? If you have ids, they belong to some resource or subset of resources, which means you need to know up from which routes they belong to, which means programmatic access won’t gain us anything in this case? I could be missing the full requirements, so let me know if I’m off base :slight_smile:

1 Like

The discoverability of the routes is the main point in this case. Basically I want to render a page (for admins-only) that a similar graphical representation to mix phx.routes (the generation of example concrete routes would just be a bonus).

1 Like

Can I propose we document this routes feature?
I’d like to use in my app to create a blocklist of invalid usernames for a root route like /username
This is common in many social apps to have a root username route.

It seems like the method is stable as it hasn’t changed in 5 years?

1 Like

Another potential use case (unless a better way is well-known to phx experts): I want to go through all the defined routes testing responses for specific users/roles. It would be good not to have to maintain a separate set of routes to test.

I’m sure that most people might recommend against it, but it seems okay to use the private __routes__ function to build this type of thing.

I’d say to not use it in a public OSS library that other people need to rely on, but for private applications and tests it seems alright. The only downside is you need to make sure it still works when you upgrade Phoenix. But you could make an upgrade checklist, or better yet write a test.

2 Likes

Right, I’ll take this - nomen omen - route since there seem to be no publicly documented way of obtaining all routes. Still wondering why this has to be “private” though. There are valid use cases for this function…

Having public access to it effectively prevents the ability to refactor. If the phoenix team finds ways to optimize the router by changing e.g. how routes are aggregated they can no longer do that without it being a breaking change. Some addtional API would retain the ability to refactor internals and staying compatible with existing consumers.

2 Likes

Something public is a reasonable ask, but as @LostKobrakai said it has maintenance implications for the team and I’m not yet convinced it’s something we want to offer. For example, in your scenario it may be nice for at-a-glance testing, but fully automating this kind of roles check is not a complete solution because the Phoenix router is not necessarily the extent of all reachable paths in the app. You could have endpoint plugs matching on the path and routing to a separate router for example, or plugs doing dynamic route dispatch based on routes stored externally, etc. We also have things like forward and get "foo/*" which operate on patterns so some level of route building is unavoidable. I’m not saying we’ll never add this, but I hope that helps give some insight :slight_smile:

1 Like

Sure. That’s a different story though. If a dev steps out of the Phoenix router, it’s his responsibility to extend the tests accordingly. Kinda Captain Obvious :slight_smile:

Understood. And NP, I can live with what I have now.

In the context of automated specific users/roles, it’s not “obvious” in that the tests give you a false sense of confidence if that’s what you’re relying on. Also note the point about having to build dynamic paths even under simple scenarios forward, get "/files/*, and even get "/org/:id/payments/:payment_id etc. Route helpers may or may not exist to help you build these, but you can’t completely automate it. Especially for “role” access it gets very tricky on who has access to what. It may be basic “admins” only on route A, B, C, but it may also be role within org having access to payments, etc so it can’t simply be route based to enforce role access. I’m not convinced the stated usecase is the best way to go about testing these things is what I’m saying :slight_smile:

Roger. I see your point.

I have a scenario that can benefit from being able to introspect the current route of a LiveView. The problem might be solvable without route introspection, so I’ve outlined the use case in the following issue, in case anyone is interested.

It didn’t make the changelog but Phoenix 1.6.x now includes Phoenix.Router.routes/1 :tada:

It was exposed in this commit:

2 Likes