cowboy snippets

Annotated Plug and Elixir Example

Tagged cowboy, elixir, erlang, genserver, otp, plug  Languages elixir, bash

This is an annotated example of a web application written using Elixir and Plug.

Create mix.exs:

#
# Mix is a build tool that provides tasks for creating, compiling, and testing
# Elixir projects, managing its dependencies, and more.
# https://github.com/hexpm/hex_web/blob/master/mix.exs
#
defmodule PlugExample.Mixfile do
  # In order to configure Mix, a developer needs to use Mix.Project in a module
  # and define a function named project that returns a keyword list with
  # configuration.
  # http://elixir-lang.org/docs/stable/mix/Mix.Project.html
  use Mix.Project

  # Configure project.
  def project do
    [app: :Router,
      version: "0.0.1",
      elixir: "~> 1.2",
      deps: deps]
  end

  #
  # Returns applications that should be started.
  #
  # In OTP, application denotes a component implementing some specific
  # functionality, that can be started and stopped as a unit, and which can be
  # re-used in other systems as well.
  # http://erlang.org/doc/man/application.html
  #
  def application do
    [
      applications: [:logger, :plug], # 3rd-party applications
      mod: {PlugExample.Supervisor, []} # own application
    ]
  end

  #
  # Returns project dependencies:
  # https://hex.pm/
  #
  def deps do
    [
      {:cowboy, "~> 1.0.0"}, # Erlang web server
      {:plug, "~> 1.0"} # Elixir's rack (Ruby), wsgi (Python), servlet (Java) API
    ]
  end
end

Create lib/router.exs

defmodule PlugExample.Router do
  #
  # Plug ships with a router that allows developers to quickly match on
  # incoming requests and perform some action:
  # https://hexdocs.pm/plug/readme.html#the-plug-router
  #
  use Plug.Router
  #
  # This module defines a Plug.Conn struct and the main functions for working with Plug connections.
  # https://hexdocs.pm/plug/Plug.Conn.html
  #
  import Plug.Conn
  # Logs request to STDOUT
  plug Plug.Logger
  # Matches request to a route
  plug :match
  # Dispatches request to a route
  plug :dispatch

  #
  # Start a GenServer, i.e., a server instance:
  #
  # http://elixir-lang.org/getting-started/mix-otp/genserver.html
  # https://blog.drewolson.org/understanding-gen-server/
  #
  def start_link do
    # Cowboy adapter:
    # https://hexdocs.pm/plug/Plug.Adapters.Cowboy.html
    {:ok, _} = Plug.Adapters.Cowboy.http PlugExample.Router, []
  end

  #
  # HTTP GET /
  #
  # Use conn.private to access route options:
  #   conn.private[:protected]
  #
  get "/", private: %{protected: false} do
    conn |> send_resp(200, "Hello world")
  end

  get "/favicon.ico" do
    conn |> send_resp(200, "LOL")
  end
  
  # Forward /about to another router's get "/" route
  # forward "/about", to: PlugExample.About

  #
  # A catch-all route
  #
  match _ do
    conn |> send_resp(404, "Not found")
  end
end

Create lib/supervisor.ex

#
# A supervisor is a process which supervises other processes, called child
# processes. Supervisors are used to build a hierarchical process structure
# called a supervision tree, a nice way to structure fault-tolerant applications.
#
# http://elixir-lang.org/docs/stable/elixir/Supervisor.html
#
defmodule PlugExample.Supervisor do
  #
  # In Elixir (actually, in Erlang/OTP), an application is a component
  # implementing some specific functionality, that can be started and stopped as a
  # unit, and which can be re-used in other systems.
  # http://elixir-lang.org/docs/stable/elixir/Application.html
  # http://erlang.org/doc/man/application.html
  # http://erlang.org/doc/design_principles/applications.html
  #
  use Application

  #
  # The type argument passed to start/2 is usually :normal unless in a
  # distributed setup where application takeovers and failovers are configured.
  #
  # start/2 typically returns {:ok, pid} or {:ok, pid, state} where pid
  # identifies the supervision tree and state is the application state.
  #
  def start(_type, _args) do
    #
    # Convenience functions for defining a supervision specification.
    # http://elixir-lang.org/docs/stable/elixir/Supervisor.Spec.html
    #
    import Supervisor.Spec, warn: false
    # A list of children (workers or supervisors) to supervise
    children = [
      worker(PlugExample.Router, [])
    ]
    #
    # If a child process terminates, only that process is restarted:
    # http://elixir-lang.org/docs/stable/elixir/Supervisor.html
    #
    opts = [strategy: :one_for_one, name: PlugExample.Supervisor]
    #
    # Start supervisor by passing the children to start_link/2.
    # You may want to use a module-based supervisor.
    Supervisor.start_link(children, opts)
  end
end

Start the server:

$ mix deps.get
$ iex -S mix

Continue reading: How to deploy an Elixir web application.