Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/oestrich/aino
An HTTP framework built on top of elli
https://github.com/oestrich/aino
elixir http
Last synced: 2 days ago
JSON representation
An HTTP framework built on top of elli
- Host: GitHub
- URL: https://github.com/oestrich/aino
- Owner: oestrich
- License: mit
- Created: 2021-08-29T03:09:34.000Z (about 3 years ago)
- Default Branch: main
- Last Pushed: 2024-08-22T15:54:28.000Z (3 months ago)
- Last Synced: 2024-10-29T08:40:11.900Z (16 days ago)
- Topics: elixir, http
- Language: Elixir
- Homepage: https://ainoweb.dev
- Size: 122 KB
- Stars: 147
- Watchers: 5
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
# Aino
[![Discord](https://img.shields.io/badge/chat-discord-7289da.svg)](https://discord.gg/wVDSWJ2EjE)
An experimental HTTP framework built on top of [elli][elli]. Aino is pronounced as "eye no".
## Why Aino?
Aino is an experiment to try out a new way of writing HTTP applications on Elixir. It uses [elli][elli] instead of Cowboy like Phoenix and Plug. Instead of writing an Endpoint like Phoenix, you write a Handler. The handler's job is to reduce across a series of middleware that are simple functions to generate a response.
The handler also works on a token instead of a conn. The token is a simple map that you can add whatever keys you wish to it. Aino has a few standard keys but you can easily ignore them if you want to write your own processing.
## How to use Aino
In order to use Aino, you must add it to your supervision tree and provide a callback handler that Aino will call `handle/1` on.
```elixir
defmodule Aino.Application do
use Applicationdef start(_type, _args) do
# get your config somehowaino_config = %Aino.Config{
callback: Example.Web.Handler,
otp_app: :example,
host: config.host,
port: config.port,
environment: config.environment,
config: %{}
}children = [
{Aino.Supervisor, aino_config}
]opts = [strategy: :one_for_one, name: Aino.Supervisor]
Supervisor.start_link(children, opts)
end
end
```In the handler, you process the incoming request (in the `token`) through a series of "middleware." The middleware all accept a single parameter, the `token`. A `token` is simply a map that you can store whatever you want on it.
The only thing that is initially pased in is the `:request`, and at the very end of the `handle/1` the token should include three keys, `:response_status`, `:response_headers`, and `:response_body`.
Aino ships with a common set of middleware that you can include at the top of processing, if you don't want them, simply don't include them! The list of middleware can be a list of lists as well.
Another built in middleware is a simple routing layer. Import the HTTP methods from `Aino.Middleware.Routes` that you're going to use in your routes. Then each HTTP method function takes the route and a middleware that should be run on the route.
```elixir
defmodule MyApp.Handler do
import Aino.Middleware.Routes, only: [get: 2, get: 3, post: 2]@behaviour Aino.Handler
def routes() do
[
get("/", &Index.index/1, as: :root),
get("/about", &Index.about/1, as: :about),
order_routes()
]
enddefp order_routes() do
[
get("/orders", &Orders.index/1, as: :orders),
get("/orders/:id", &Orders.show/1, as: :order),
post("/orders", &Orders.create/1)
]
end@impl true
def handle(token) do
middleware = [
Aino.Middleware.common(),
&Aino.Middleware.Routes.routes(&1, routes()),
&Aino.Middleware.Routes.match_route/1,
&Aino.Middleware.params/1,
&Aino.Middleware.Routes.handle_route/1,
]Aino.Token.reduce(token, middleware)
end
end
```The route middleware take a token and generally should return the three keys required to render a response. You can also render EEx templates as shown below.
```elixir
defmodule Index do
alias Aino.Tokendef index(token) do
token
|> Token.response_status(200)
|> Token.response_header("Content-Type", "text/html")
|> Token.response_body(Index.View.render("index.html"))
end
enddefmodule Index.View do
require Aino.ViewAino.View.compile [
"lib/index/index.html.eex"
]
end
```## Concepts
### `Aino.Handler`
A handler processes an incoming request from Aino.
The `handle/1` function is passed an `Aino.Token`.
The handler _must_ return a token that contains three keys to return a response:
- `:response_status`
- `:response_headers`
- `:response_body`If the token does not contain these three keys, a 500 error is returned.
Inside your handler, you may wish to use several `Aino.Middleware` including
`Aino.Middleware.common/0`.### `Aino.Token`
The token is what flows through the entire web request. Tokens are simple maps
that contain no defined keys beyond `:request`. Several Aino middleware add
keys and they are documented in the functions.### `Aino.Middleware`
Middleware are simple functions that take the token and return the token. They process
the request and add or modify existing keys on the token.An example middleware is `Aino.Middleware.headers/1`:
```elixir
def headers(%{request: request} = token) do
headers =
Enum.map(request.headers, fn {header, value} ->
{String.downcase(header), value}
end)Map.put(token, :headers, headers)
end
```[elli]: https://github.com/elli-lib/elli