Plug modularity - is there a way to split the routes in different modules so that they are all evaluated until a matching route is found?

I’m building a web API using only Plug (no Phoenix). I started small but now it is growing and the number of routes is growing too.
I started to use forward to extract some routes to a proper module.

What I don’t like about forward is that it creates mutually exclusive routes.

Do you know if there is a different way to split the routes in different modules so that they are all evaluated until a matching route is found?

Why?
Because I would like to group routes in terms of the functionality not in terms of route url.
For example I want /companies to be in the Company module and /companies/:id/employees in the Employee module (and I don’t like to double forward :slight_smile: )

Thank you

1 Like

For organization, you can split the routes into different modules that implement a macro, then use that macro in your main router module. That way, all the routes end up in the main router after compilation.

Something like this:

defmodule App.Routes do
  import App.Routes.{Web, Api}

  web_routes()
  api_routes()
end

defmodule App.Routes.Web do
  defmacro web_routes do
    quote do
      # ...module routes here...
    end
  end
end

defmodule App.Routes.Api do
  defmacro api_routes do
    quote do
      # ...module routes here...
    end
  end
end
3 Likes

I get an error on the two calls to web_routes() and api_routes():

== Compilation error in file lib/web/main_router.ex ==
** (CompileError) lib/web/main_router.ex:27: undefined function conn/0
    (elixir) src/elixir_locals.erl:108: :elixir_locals."-ensure_no_undefined_local/3-lc$^0/1-0-"/2
    (elixir) src/elixir_locals.erl:109: anonymous fn/3 in :elixir_locals.ensure_no_undefined_local/3
    (stdlib) erl_eval.erl:680: :erl_eval.do_apply/6

probably I’m missing something… :thinking:

You likely have to use unquote(conn) inside your macros but it depends how you wrote them.

One of the modules is this:

defmodule App.Routes.Api do
  defmacro api_routes do
    quote do
      get "/bar" do
        send_resp(conn, 200, "bar")
      end
    end
  end
end

I tried to unquote conn but I get another error (variable “conn” does not exist…)

Hello,
I think you need to pass conn to your macro :

defmodule App.Routes.Api do
  defmacro api_routes(conn) do
    quote do
      get "/bar" do
        send_resp(unquote(conn), 200, "bar")
      end
    end
  end
end

And of course invoke it like :

api_routes(conn)
1 Like

What a fool I am! :disappointed:

You are right, I didn’t think at that.
Thank you. It works.

1 Like