New Router features in Plug 1.3

Plug v1.3.0 has just been released. Read on for a couple of interesting new features.

Plug is a simple specification and powerful library for writing composable web modules in Elixir. While it underpins the popular Phoenix Framework, it also tends to get over-shadowed. And yet, Plug is very capable on its own, and has a couple of new features that are helping it to pull even.

Here is an example of a module that satisfies the Plug spec. It only needs to export two functions: init/1 and call/2.

defmodule HelloPlug do
  import Plug.Conn

  def init(opts) do

  def call(conn, _opts) do
    send_resp(conn, 200, "hello")


The centerpiece of most web frameworks is the router. It provides the bridge between the framework’s code and the user’s by dispatching each request (identified by a URL path) to a designated resource implementation. In most Elixir web frameworks, the router provides an approachable DSL for defining the routing structure. Plug is no exception here.

Since version 1.0, Plug’s Router has had a DSL reminiscent of Sinatra. It defines a macro for each HTTP verb, which matches a URL path and executes a body of code operating on the connection.

defmodule MyApp.Router do
  use Plug.Router

  plug :match
  plug :dispatch

  get "/hello" do
    send_resp(conn, 200, "world")

In contrast, Phoenix’s Router is more in the style of Ruby on Rails. It dispatches each request to an action function in a Controller module.

defmodule MyApp.Router do
  use HelloPhoenix.Web, :router

  get "/hello", HelloController, :show

Underneath, both Plug and Phoenix’s routers implement the Plug module specification. However, in the case of Phoenix, an extra bit of cleverness lurks behind its MVC facade: Controllers are Plugs! In fact, the router macros will accept any Plug module. We could, for example, change the route to:

get "/hello", HelloPlug, [an_option: :a_value]

Plug’s Router has been deprived of a feature like this, until now. Starting in v1.3.0, you can dispatch requests directly to a Plug module:

get "/hello", to: HelloPlug, init_opts: [an_option: :a_value]

This is equivalent to:

get "/hello" do, HelloPlug.init([an_option: :a_value]))

While a seemingly small change, this feature will make it both easier and more likely for us to build Controller-like patterns directly on top of Plug Router. With it you can factor out the code that handles each route into separate modules, rather than filling up your router with function bodies.

Path Params

Plug Router’s routes have always supported dynamic path segments, such as:

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

The :name parameter can be used directly as a variable in the function body passed to the match macro. In order to make these values available generally in the connection, Plug 1.3 has added a conn.path_params map to hold them. They will also be saved in conn.params, a behavior which will be familiar to Phoenix users.

The Plug Router docs have all the details.

P.S. The reason I decided to blog about these new Plug features is because I wrote them!


Posted by Christopher Adams

Christopher Adams is a writer, publisher, and software developer. He works on free software and free culture, and specializes in photography and web technologies.

Read the source code for this page.