# Tokumei.Router
**Focused routing layer for Raxx applications**
1. DSL for routing incomming HTTP requests by path and method.
2. Extension to the Raxx interface to provide better error handling.
3. Concept of a middleware stack.
## Installation
If [available in Hex](https://hex.pm/docs/publish), the package can be installed as:
1. Add `tokumei_router` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:tokumei_router, "~> 0.3.0"},
{:ace_http, "~> 0.1.2"}
]
end
```
2. Ensure `tokumei_router` is started before your application:
```elixir
def application do
[applications: [:tokumei_router, :ace_http]]
end
```
*Ace.HTTP is a server that can host Raxx applications,
adapters are also available for [cowboy](https://hex.pm/packages/raxx_cowboy) and [elli](https://hex.pm/packages/raxx_elli).*
## Usage
```elixir
# my_app/router.ex
defmodule MyApp.Router do
use Tokumei.Router
alias Raxx.Response
# use middleware by specifying module and configuration.
# ContentLength is included as an example.
@middleware {Tokumei.Router.ContentLength, nil}
# Define response actions inline using Raxx Helpers.
route "/hello" do
:GET -> Response.ok("Hello, World!")
end
# Match on string variables
route "/hello/:name" do
:GET ->
greeting = "Hello, #{name}!"
Response.ok(greeting)
end
# Define actions for multiple methods on a resource.
route "/users/:id" do
:GET -> UsersController.show(id, request)
:POST -> UsersController.create(id, request)
:DELETE -> UsersController.delete(id, request)
end
# Access the request and environment passed to the router
route "/users" do
:GET ->
%{page: page_number} = request.query
users_page = env.user_repo.all(page_number: page_number)
Response.ok(users_page)
end
# Handle routing errors
error %NotFoundError{request: request} do
Response.not_found("not found: #{inspect(request.path)}")
end
error %MethodNotAllowedError{allowed: allowed} do
Raxx.Response.method_not_allowed([{"allow", allowed |> Enum.join(" ")}])
end
# return custom errors
route "/sign-up" do
:POST ->
case validate_sign_up_form(request.body) do
{:ok, data} ->
Response.created()
# continue
{:error, _reason} ->
{:error, :bad_request}
end
end
error :bad_request do
Response.bad_request()
end
# Mount subapp
mount "/api", APIRouter
end
```
Choose a server to host the application.
Using `Tokumei.Router` will create a Raxx App that can be mounted using any of the adapters found in [Raxx](https://github.com/CrowdHailer/raxx)
```elixir
defmodule MyApp do
use Application
def start(_type, _args) do
import Supervisor.Spec, warn: false
children = [
worker(Ace.HTTP, [{MyApp.Router, []}, [port: 8080]])
]
opts = [strategy: :one_for_one]
Supervisor.start_link(children, opts)
end
end
```
The `Tokumei.Router` provides a simple routing DSL that first routes on path and second on request method.
This is done so that `Tokumei.Router` can return the correct responses for resources that will respond to only a subset of HTTP verbs.
```elixir
import Raxx.Request
assert 200 == MyApp.Router.handle_request(get("/hello"), :env)
assert 405 == MyApp.Router.handle_request(put("/hello"), :env)
assert 404 == MyApp.Router.handle_request(put("/random"), :env)
```