Raxx: an Elixir webserver interface

What is Raxx?

  1. An interface specification for Elixir webservers and Elixir application.
  2. A set of tools to help develop Raxx-compliant web applications

Raxx is inspired by the Ruby's Rack interface and Clojure's Ring interface.

[Documentation for Raxx is available online](TODO hex)

Usage

Raxx handlers

A Raxx handler is a module that has a handle_request function. It takes two arguments, a raxx request and an application specific environment. The return value is a map with three keys, the status, the headers, and the body.

Minimal

With the power of Elixirs pattern matching against maps it is possible to handle request routing without a dsl.

defmodule BasicRouter do
# handle the root path
def handle_request(%{path: [], method: "GET"}, _env) do
%{status: 200, headers: %{}, body: "Hello, World!"}
end
# forward to a sub router
def handle_request(request = %{path: ["api" | rest]}, env) do
ApiRouter.handle_request(%{request | path: rest}, env)
end
# handle a variable segment in path
def handle_request(%{path: ["greet", name], method: "GET"}, _env) do
%{status: 200, headers: %{}, body: "Hello, #{name}"}
end
end

Manually creating all these response hashes can be tedious so the Response module has helpers.

defmodule FooRouter do
import Raxx.Response
def handle_request(%{path: ["users"], method: "GET"}, _env) do
ok("All user: Andy, Bethany, Clive")
end
def handle_request(%{path: ["users"], method: "POST", body: data}, _env) do
case MyApp.create_user(data) do
{:ok, user} -> created("New user #{user}")
{:error, :already_exists} -> conflict("sorry")
{:error, :bad_params} -> bad_request("sorry")
{:error, :database_fail} -> bad_gateway("sorry")
{:error, _unknown} -> internal_server_error("Well thats weird")
end
end
def handle_request(%{path: ["users"], method: _}, _env) do
method_not_allowed("Don't do that")
end
def handle_request(%{path: ["users", id], method: "GET"}, _env) do
case MyApp.get_user(id) do
{:ok, user} -> ok("New user #{user}")
{:error, nil} -> not_found("User unknown")
{:error, :deleted} -> gone("User deleted")
end
end
def handle_request(_request, _env) do
not_found("Sorry didn't get that")
end
end

Raxx Server Sent Events

See sever sent events in examples directory.

defmodule ServerSentEvents.Router do
import Raxx.Response
# Can't use ServerSentEvents Handler in same module as other Streaming handlers.
import Raxx.ServerSentEvents
def handle_request(%{path: [], method: "GET"}, _opts) do
ok(home_page)
end
def handle_request(%{path: ["events"], method: "GET"}, opts) do
upgrade(opts, __MODULE__)
end
def handle_request(_request, _opts) do
not_found("Page not found")
end
def handle_upgrade(_options) do
Process.send_after(self, 0, 1000)
event("hello")
end
def handle_info(10, _opts) do
close()
end
def handle_info(i, _opts) when rem(i, 2) == 0 do
Process.send_after(self, i + 1, 1000)
event(Integer.to_string(i))
end
def handle_info(i, _opts) do
Process.send_after(self, i + 1, 1000)
no_event
end
defp home_page do
"""
The page. see example.
"""
end
end

Some outstanding questions about Server Sent Events functionality.

Link to implementing server in node.js

HTML living standard

Installation

If available in Hex, the package can be installed as:

  1. Add raxx to your list of dependencies in mix.exs:

    def deps do [{:raxx, "~> 0.0.1"}] end

  2. Raxx apps/routers needs to be mounted Elixir/erlang server using one of the provided adapters. Instructions for this are found in each adapters README

- [cowboy](https://github.com/CrowdHailer/raxx/tree/master/example/cowboy_example). Currently just follow example.

Contributing

If you have Elixir installed on your machine then you can treat this project as a normal mix project and run tests via mix test.

If required a development environment can be created using Vagrant.

Principles