Phoenix Router Inline Controllers

Plug’s Router enables you to write functions “inline” and even access the variables defined in the route path:

get "/hello/:name" do
  send_resp(conn, 200, "hello #{name}")
end

Does phoenix enable you to do something like this? I want to keep using all of Phoenix helpers, but for simple stuff it would be nice to be able to code in a micro-framework style, and move to things to controllers/views/etc as the project grows.

Note to Forum Admins: Add plug tag.

3 Likes

Our router does not support this, but you can do something like forward all unhanded requests in the Phoenix router to your Plug.Router, or vice versa by forwarding all unhanded Plug router requests to the Phoenix Router. It’s turtles all the way down: i.e.:

defmodule MyApp.Router do

  ...
  get "/", PageController, :index
  forward "/", MyApp.PlugRouter
end

defmodule PlugRouter do
  use Plug.Router

  get "/hello/:name" do
    send_resp(conn, 200, "hello #{name}")
  end
end 
5 Likes

Chris,

Awesome solution! So simple. From a marketing perspective it would be interesting if Phoenix supported this more natively someday: Phoenix - A micro/macro framework that scales as your code base and users grow.

I heard that there are plans for the code generators to create even smaller projects, this could help to remove the controllers folder if needed. I you could optionally code in Plugs style, the most bare-bone Phoenix project would just need a web folder like this

web
| - router.ex
| - web.ex

Is there a way to enable this behavior when to get, post, put, etc, macros have a do block?

There is a certain appeal to microframeworks, single file, functions are routes, etc. Phoenix could totally nail this.

Getting an error with @chrismccord answer:

Here is my Plug Router

defmodule PlugRouter do
  use Plug.Router

  plug :fetch_session

  get "/hello" do
    IO.inspect("Hello World")
    send_resp(conn, 200, "world")
  end

end

Added this to the phoenix router

forward "/plug", PlugRouter

I am getting this error on localhost:4000/plug/hello

Server: localhost:4000 (http)
Request: GET /plug/hello
** (exit) an exception was raised:
** (Plug.Conn.NotSentError) no response was set nor sent from the connection
(plug) lib/plug/adapters/cowboy/handler.ex:42: Plug.Adapters.Cowboy.Handler.maybe_send/2
(plug) lib/plug/adapters/cowboy/handler.ex:16: Plug.Adapters.Cowboy.Handler.upgrade/4
(cowboy) src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4

The term “micro framework” is a pointless description imo. If you want a library and want to stitch code together then libraries are fine, but we lie to ourselves calling things “micro frameworks” and then folks somehow put negative connotations then to “frameworks”. Here’s an entire Phoenix app:

defmodule MyApp.Endpoint do
  use Phoenix.Endpoint

  def call(conn, _) do, do: Plug.Conn.send_resp(conn, 200, "micro!")
end

We could generate a project with just this file, your supervision tree, and a mix.exs, but it wouldn’t be useful for anything other than showing how micro we are :slight_smile:
Phoenix has always been about sane defaults balancing features and modularity. Every decision to add a directory or file to the default project structure is weighed heavily. You are of course free to not use “controllers”, but the moment your app grows you’ll end up with a “plugs” or a “routers” directory that is doing the same thing as what we call controllers, so pick your poison.

6 Likes

Yeah, literally don’t mean “single file” as in Flask, mix phoenix.new does enough to help you get started quickly, but there are people (including myself) who feel that for simple things the complete folder structure is too heavy-weight, see this reddit thread.

@chrismccord Any idea of why I am getting the error previously mentioned? This is on Phoenix 1.0.2, I’ll test in a more recent version.

Edit: Fixed the reddit thread link.

I am getting this error in 1.1.4

== Compilation error on file web/router.ex ==
 ** (RuntimeError) no plugs have been defined in PlugRouter
     (plug) expanding macro: Plug.Builder.__before_compile__/1
     web/router.ex:15: PlugRouter (module)
     (elixir) lib/kernel/parallel_compiler.ex:100: anonymous fn/4 in    Kernel.ParallelCompiler.spawn_compilers/8

Is this module a Plug?

I was missing

  plug :match
  plug :dispatch
3 Likes