Crowdhailer
Raxx - Interface for HTTP webservers, frameworks and clients (1.0 now released!)
Raxx is an alternative to Plug and is inspired by projects such as Rack(Ruby) and Ring(Clojure).
1.0-rc.1is now available. To use it requires a server, the examples here can be experimented with by adding{:ace, "~> 0.15.2"}to a projects dependencies, NOTE1.0-rc.0is equivalent to0.14.0
Raxx models HTTP as message passing between client and server. There are two flavours to this model.
First is the simple case where a complete request is a single message from client to server and a complete response as a single message from server to client. This is the model successfully exploited by Rack.
The second cases is for streaming where a series of messages are sent in either/both directions. For HTTP this series of messages begins with a head, consisting of all header information plus a few extra details (path for request, status for response). There is then the body broken into 1 or more parts and finally a tail which may contain no further information or may include some trailers.
Simple
request -->
Client ============================================ Server
<-- response
A server can implement the handle_request/2 callback to use this model in the simplest cases.
For example:
defmodule HomePage do
use Raxx.Server
@impl Raxx.Server
def handle_request(%{method: :GET, path: []}, _state) do
response(:ok)
|> set_header("content-type", "text/plain")
|> set_body("Hello, World!")
end
end
Streaming
tail | data(1+) | head(request) -->
Client ============================================ Server
<-- head(response) | data(1+) | tail
A server can implement the handle_head/2, handle_data/2 & handle_tail/2 callbacks to react to parts of the request as they become available
defmodule Upload do
use Raxx.Server
@impl Raxx.Server
def handle_head(%{method: :PUT, path: ["upload"] body: true}, _state) do
{:ok, io_device} = File.open("my/path")
{[], {:file, device}}
end
@impl Raxx.Server
def handle_data(data, state = {:file, device}) do
IO.write(device, data)
{[], state}
end
@impl Raxx.Server
def handle_tail(_trailers, state) do
response(:see_other)
|> set_header("location", "/")
end
end
Raxx.Server has one final callback handle_info/2 for dealing with updates from other processes within the application. Several more examples are available in the README.
Goals
Raxx is designed as an alternative to Plug but not a replacement. This thread is meant to be just about Raxx developments, see this previous topic for why I started developing an alternative to plug.
- model the communication between Client and Server as messaging to be consistent with the erlang/Elixir world view.
This is the main difference to plug which uses a connection model. - support all HTTP patterns including streaming of requests and/or responses.
In this way it is an evolution of Rack. - Require only pure functions, even when implementing a stateful server.
Familiar anyone who has implemented aGenServer.
Most Liked
Crowdhailer
This release ships with no new exciting features, hooray!
All deprecation warnings from 0.x versions have been removed.
For conversations pre 1.0 see this thread.
Simple web tookit
Raxx is a toolkit to make web application with Elixir simple.
It is designed to get out of the way and let you develop any kind of application.
https://github.com/crowdhailer/raxx
Thanks
To everyone who is using Raxx and taken the time to give feedback, thanks.
If you want to join in, check the slack channel.
Extra special thanks to all contributors.
Next
With the core stabilised priorities can now shift.
Ecosystem
The Raxx ecosystem already has support for logging, static files, cors, sessions and more.
Each of these extensions to the core library can now drive towards their own 1.0 release.
Documentation
This is something that can always be improved,
however now the core API is stable efforts documentation and guides will be accurate for much longer.
Getting started
Raxx.Kit is the quickest way to get started with raxx projects.
I want to work on this to increase it’s utility and get newcomers moving even faster.
Further integrations
Finally I hope to spend some time on some more ambitious experiments;
An integration with Absinthe would be interesting as Raxx is great when just building an API service.
Drab and Texas are also libraries that I would like to try out.
Perhaps the most ambitious, because I don’t know what the endpoint looks like, is to try and do more with GenBrowser. The idea of actors on the client and server is a very appealing prospect.
Help wanted
I won’t be able to do all of the next steps on my own.
Contributions are always welcome and now is a great time to get involved.
Documentation is always a good place to start or maybe you have a particular integration that you need.
I am very happy to help you if you want to get started contributing.
Updating
To use 1.0 update any dependent libraries that you need, to the following versions.
These are all non-breaking changes with respect to their previous versions.
# mix.exs
defp deps do
[
{:ace, "~> 0.18.6"}
{:raxx_logger, "~> 0.2.2"}
{:raxx_view, "~> 0.1.4"}
{:raxx_static, "~> 0.8.3"}
{:raxx_method_override, "~> 0.4.1"}
]
end
Otherwise you can use {:raxx, "~> 1.0", override: true}.
Crowdhailer
Raxx now has a simple client for HTTP/1.1
Examples
synchronous
request = Raxx.request(:GET, "http://example.com")
|> Raxx.set_header("accept", "text/html")
{:ok, response} = Raxx.SimpleClient.send_sync(request, 2000)
asynchronous
request = Raxx.request(:GET, "http://example.com")
|> Raxx.set_header("accept", "text/html")
channel = Raxx.SimpleClient.send_async(request)
{:ok, response} = Raxx.SimpleClient.yield(channel, 2000)
Simple Client
Client is simple because it makes very few assumptions about how to deal with requests and responses.
For example.
- Cookies are not managed, each request is handled in isolation
- Connections are not limited or reused, each request gets a new connection
These omissions of functionality make the client much simpler to work with and reason about.
They are also not limitations in many cases. An API client probably doesn’t use cookies.
Composable requests
Because a Raxx.Request is just a data structure,
using this client separates the logic of building the request from the side effect of sending it.
This makes it very easy add custom functionality for your own clients.
import Raxx
def set_request_id(request, request_id) do
request
|> set_header("x-request-id", request_id)
end
def set_json_payload(request, json) do
request
|> set_header("content-type", "application/json")
|> set_body(Poison.encode!(json))
end
# later
request = Raxx.request(:POST, "http://api.com/create_user")
|> set_request_id("12345")
|> set_json_payload(%{"username" => "alice"})
The value of this separation for me in my own projects has been with testing.
It’s now very easy to create invalid requests. just forget to set a request_id and then send it to the API.
Raxx 0.15.8 adds Raxx.SimpleClient
Crowdhailer
Core library streamlined
From 0.18.0 the core raxx library is focusing on being an interface.
This is in preparation for a stable 1.0 release. This focus allows extensions such as session and cores can continue to be iterated upon.
When upgrading it is advised to upgrade to 0.17.6 and handle warnings before moving to 0.18.0
See CHANGELOG for details
Popular in Announcing
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








