Sanjibukai
Constraints in Routes
Hi everybody,
I wanted to know if we can have constraints in the route level. I mean defining a route with some constraints (based e.g. on the request sessions data).
Sorry if I do a parallel with rails, but in rails (as explained from here in the guides) we can have constraints on the routes.
The constraints I’m particularly interested in are those based on lambdas.
Let me give you an example use case I had in rails..
I have different kind of users (regular, admins, etc.) where I have its type stored in a session variable once logged in.
Then I can do the following:
constraints lambda { |req| req.session[:user_type] == 'Admin' } do
get '/dashboard', to: 'admin_dashboard#show', as: 'dashboard'
...
end
constraints lambda { |req| req.session[:user_type] == 'User' } do
get '/dashboard', to: 'user_dashboard#show', as: 'dashboard'
...
end
This example is a little extract and might not tell all the story but as you might noticed it’s somehow an authorization mechanism.
I can now have some resources controllers scoped to the type of users and I know that, thanks to the constraints at the route level, I will never have some authorization mishaps.
In more complex situations I can have only the show action for a user while having the whole resources actions for an admin.
Doing it like this let me not bother at all at the controller level and hence I don’t have the need to import any authorization package or code a complex custom authorization handler.
In the Router doc page, there is no mention at all of the word constraint.
So, do you know how I can do this?
Note: elixir being functional and Phoenix being based on plugs, I’m pretty sure that I can easily achieve this kind of constraint based routes, thanks to plugs, pipelines, forwarding routes and pattern matching, right? I just need some guidance.
Most Liked
LostKobrakai
The phoenix router can be so fast, because it works by harnessing elixir’s pattern matching. Therefore a certain url either matches to a path registered in the router or not. If you have multiple routes matching the first one will win. For the pattern match the only variables to match on are the path, host and method.
Given those constraints you cannot handle the logic you’re looking for in a phoenix router. This is not to say that you cannot nest your plug hierarchy more to get to the same resulting functionality. You could have one router match the path /dashboard, which forwards to a plug, which depending on your constraints decides which controller to call.
LostKobrakai
I was thinking about something akin to this.
# router
get '/dashboard', ControllerByTypePlug, action: :show, controller: %{
admin: Admin.DashboardController,
user: User.DashboardController
}
# plug
defmodule MyApp.ControllerByTypePlug do
import Plug.Conn
def init(opts) do
action = Keyword.fetch!(opts, :action)
controller = Keyword.fetch!(opts, :controller)
unless [:admin, :user] in Map.keys(controller) do
raise "missing controller mapping"
end
%{action: action, controller: controller}
end
def call(conn, %{action: action, controller: controller}) do
case type_to_atom(conn["session"]["user_type"]) do
{:ok, type} ->
controller = Map.fetch!(controller, type)
controller.call(conn, action)
{:error, _} -> # handle error
end
end
defp type_to_atom(str) when str in ["admin", "user"], do: {:ok, String.to_atom(str)}
defp type_to_atom(str), do: {:error, str}
end
But if you can have separate routers per user type that’s even simpler and imho the even cleaner solution.
LostKobrakai
Phoenix might be constraint in what you can do directly on the router level, but on the otherhand as everything is a plug you can quite easily nest/switch out stuff.
# endpoint
plug :fetch_session # So session is available
# plug MyAppWeb.Router
plug MyAppWeb.Plug.DependantRouter
# This one forwards to the actual routers.
defmodule MyAppWeb.Plug.DependantRouter do
import Plug.Conn
def init(options), do: options
def call(conn, _opts) do
case conn["session"]["user_type"]
"admin" -> AdminRouter.call(conn, [])
"user" -> UserRouter.call(conn, [])
...
end
end
end
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








