Simple Web Servers with Plug and Cowboy

Will Raxworthy
AlphaSights Engineering
4 min readMar 7, 2016

If you want to build an Elixir web application there’s no doubt that the Phoenix framework is the most full featured option out there. However, if you’re looking for something a little simpler, like an endpoint for web hooks, then there are some alternatives. In this post we’re going to look at how to use Cowboy and Plug. Cowboy is a small web server written in Erlang. It’s fast and built to be modular. Plug is very similar to Rack (if you’re coming from Ruby). It’s a specification for creating modules that you can use in varying web applications and also provides connection adapters with web server like Cowboy.

In this post, I’ll show you how to set up a simple supervisor, router and some endpoints.

The first thing we’ll do is create a new Elixir application:

$> mix new plugger --sup
$> cd plugger

We’ll only add two dependencies to this application. Plug and Cowboy. Both of these are Elixir applications themselves, so we’ll need to start them up. Your mix.exs file should look like this:

Run mix deps.get to install both dependencies. Next we’ll create the file that will contain the applications endpoints, I’ll refer to this file as the router.

The first two plugs here demonstrate the great power of Plug. When the router is invoked it will first match the incoming request against your matchers (get “/”, do) and then the dispatch plug will execute the code within the match block. It’s also possible to define our own plugs that will run on the way to the matchers in the router.

The second part here is the start_link function which starts the router and returns a pid to be monitored by the application supervisor.

In order to get the server to start accepting requests, let’s start simple. Add the following to the router file.

plug :match
plug :dispatch
get "/" do
conn
|> send_resp(200, "Plug!")
end

At this point, I would recommend downloading Postman. It’s a mac app that makes making requests much easier. You could also use curl or any other http client.

Boot the app by running mix run — no-halt. By default the server will run on http://localhost:4000. Hitting this url should return the word “Plug!”.

I mentioned earlier that you can write your own plug functions that run during the request process. As an example, adding another plug called say_hello/2 and then add this to your router.

plug :say_hellodef say_hello(conn, _params) do
IO.puts "hello!"
conn
end

Any plug placed within the router will receive two arguments, the connection and the parameters associated with the request. Every plug method must return the connection.

You can also create Plug modules. A Plug module is any module that implements a init/1 and a call/2 functions. Implementing the say_hello plug above would be converted in to a module like this:

The result of this would be a log in the console whenever a request comes in. You’ll notice that we also passed in arguments when we added it to the router. This is handy to create reusable Plugs or plugs that require things like tokens to run an authentication check.

Lets look at an example that takes a get request and authenticates the request using a token previously set under “Authorization” header. We’ll start with the router. It’s important to note here that the Plug.Authenticate module occurs before the match and dispatch plugs and that we pass the initial token in as an argument.

All we’ve added to the route is a matcher for a get request to the base path and a plug that will authenticate the request. Here is the Plugger.Authenticate file:

We start by accepting and returning the default options. These options include the token we need to validate against. In the call function, the connection through a series of pipes. The first function fetches the token from the headers and returns a tuple with the connection and the fetched token. Next, the tuple is passed in to the authenticate function along with the token we received from the initial options. We rely on pattern matching to validate the token.

def authenticate({conn, [token]}, token) do
conn
end
def authenticate({conn, _}, token) do
conn
|> Plug.Conn.send_resp(401, “Not Authorised”)
|> Plug.Conn.halt
end

In the first function, if the two values that match token are exact matches, then the connection is allowed to continue out of our Plug module and in to the next plug defined in the router. If they don’t match, the second function catches it, sends a 401: Not Authorized and halts the connection.

This is only an initial look at some of the interesting things you can do with Plug. If you’re using Phoenix , you can start doing these types of things in that application. If there are a few repeated actions taking place in your controllers that run on the connection, you could look at moving these in to plugs and running them before they get to your controller. Plug is also great alternative to using Phoenix for small applications that only have a few simple endpoints.

In another post shorty, I’ll talk about using pipelines to create groups of plugs that run you can apply to specific request scenarios.

--

--