Getting Started

The first part we will build is an entrypoint to the api. The entrypoint serves as the gateway for all requests. Any request to the webserver will run the code specified in the entrypoint. Examples of things one might put in an entrypoint include: Body Parsing, Cors handling, Https redirects, Authorization etc. In the entrypoint we will put an entry to direct requests to our router (more on that one later). The router is what will direct requests to the requested resource or action.

Setting up the entrypoint

The entrypoint is built using the Plug.Builder. Plug.Builder is a useful module for building so called request pipelines. A request pipeline is a list of static definitions of operations to apply to a request, and can include some of the things stated in the prelude above. The primary directive of the Plug module is the macro Plug.plug/1, which is used to declare an operation in a pipeline.

The following is the entrypoint, below is an explanation of what each of these operations do:

  • The Plug.Logger module logs metadata about incoming requests.
  • The Plug.MethodOverride module overrides a POST requests method, if another method is specified in the _method header of the request.
  • The Plug.Head module overrides HEAD requests to GET requests.
  • The last plug directs requests into the Router of our app.
defmodule MyApp.Entrypoint do
  use Plug.Builder

  plug Plug.Logger
  plug Plug.MethodOverride
  plug Plug.Head

  plug MyApp.Router
end

Setting up the webserver

The webserver is the application we will be running. In effect, this will be what is running our API, listening for incoming requests from the internet. Setup is fairly straightforward and consists mostly of configuration. The configuration we have set tells the webserver to listen for HTTP requests on port 8080, and direct them to our entrypoint.

defmodule MyApp.WebServer do
  use Supervisor

  def start(_type, _args) do
    children = [
      Plug.Cowboy.child_spec(scheme: :http, plug: MyApp.Entrypoint, options: [port: 8080])
    ]
    
    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

Starting the webserver with mix.

Adding the following to our mix.exs will start the webserver by running the command mix in the command-line. We add logger as an additionational application, otherwise Plug.Logger will not function properly.

  def application do
    [
      mod: {Tavernan.Application, []},
      extra_applications: [:logger]
    ]
  end

Setting up the router

Honeybee is a router package. In the router we will be declaring a honeybee router. This is done using the Honeybee module. Honeybee introduces a DSL for declaring routes. Each route can declare a unique plug pipeline, similar to the pipelines declared in Plug.Builder. Honeybee however provides us with the possibility to customize pipelines seperately for each route, thus being able to invoke custom behaviour on each route. The honeybee router itself is a plug, which is why we can invoke it as a plug from the entrypoint. You can find details about the Honeybee DSL in the Honeybee module docs. The router definition below is very bare bones, and will serve a 404 to any incoming request. As an exercise we will fill this router with more routes in the following sections, as well as providing meaningful patterns for building more routes.

defmodule MyApp.Router do
  use Honeybee

  match _, "*", do: plug :not_found
  
  def not_found(conn, _opts) do
    Plug.Conn.resp(conn, 404, "")
  end
end