# 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](http://rack.github.io/) and [Clojure's Ring interface](https://github.com/ring-clojure).*
[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.
```elixir
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.
```elixir
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.
```elixir
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.
- [ ] Disallow event of type error.
- [ ] Handle long poll pollyfill.
- [ ] Raxx client.
- [ ] Any shared functionality with file streaming, long pole.
- [ ] What to do if message handler throws error.
[Link to implementing server in node.js](http://www.html5rocks.com/en/tutorials/eventsource/basics/)
[HTML living standard](https://html.spec.whatwg.org/multipage/comms.html#server-sent-events)
## Installation
If [available in Hex](https://hex.pm/docs/publish), 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](www.vagrantup.com).
### Principles
- Stateless HTTP request fulfill a valuable role in modern applications and will continue to do so.
- Handling other communication patterns as plug intends to do just adds complexity which is unnecessary on a whole class of applications.
- Use Ruby rack and Clojure ring as inspiration for naming but be happy to break away from historic CGI-style header names.
- Surface utilities so that it can be used in general HTTP based applications
- Only return json if json is asked for.
- [Your server as a function](https://monkey.org/~marius/funsrv.pdf)