{"id":19821590,"url":"https://github.com/oestrich/aino","last_synced_at":"2025-04-06T09:11:22.455Z","repository":{"id":36981595,"uuid":"400938821","full_name":"oestrich/aino","owner":"oestrich","description":"An HTTP framework built on top of elli","archived":false,"fork":false,"pushed_at":"2024-08-22T15:54:28.000Z","size":125,"stargazers_count":148,"open_issues_count":0,"forks_count":0,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-03-30T08:09:09.148Z","etag":null,"topics":["elixir","http"],"latest_commit_sha":null,"homepage":"https://ainoweb.dev","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/oestrich.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"oestrich","patreon":"ericoestrich"}},"created_at":"2021-08-29T03:09:34.000Z","updated_at":"2024-12-01T19:52:38.000Z","dependencies_parsed_at":"2023-12-07T21:29:12.902Z","dependency_job_id":"344bef1d-374c-47ac-bbd8-ca758ef11f83","html_url":"https://github.com/oestrich/aino","commit_stats":{"total_commits":123,"total_committers":2,"mean_commits":61.5,"dds":0.04878048780487809,"last_synced_commit":"c5b76390e988a41b08f577f86afe5d21150b8a3c"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oestrich%2Faino","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oestrich%2Faino/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oestrich%2Faino/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oestrich%2Faino/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oestrich","download_url":"https://codeload.github.com/oestrich/aino/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247457803,"owners_count":20941906,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elixir","http"],"created_at":"2024-11-12T10:29:35.500Z","updated_at":"2025-04-06T09:11:22.438Z","avatar_url":"https://github.com/oestrich.png","language":"Elixir","readme":"# Aino\n\n[![Discord](https://img.shields.io/badge/chat-discord-7289da.svg)](https://discord.gg/wVDSWJ2EjE)\n\nAn experimental HTTP framework built on top of [elli][elli]. Aino is pronounced as \"eye no\".\n\n## Why Aino?\n\nAino 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.\n\nThe 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.\n\n## How to use Aino\n\nIn order to use Aino, you must add it to your supervision tree and provide a callback handler that Aino will call `handle/1` on.\n\n```elixir\ndefmodule Aino.Application do\n  use Application\n\n  def start(_type, _args) do\n    # get your config somehow\n\n    aino_config = %Aino.Config{\n      callback: Example.Web.Handler,\n      otp_app: :example,\n      host: config.host,\n      port: config.port,\n      environment: config.environment,\n      config: %{}\n    }\n\n    children = [\n      {Aino.Supervisor, aino_config}\n    ]\n\n    opts = [strategy: :one_for_one, name: Aino.Supervisor]\n    Supervisor.start_link(children, opts)\n  end\nend\n```\n\nIn 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.\n\nThe 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`.\n\nAino 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.\n\nAnother 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.\n\n```elixir\ndefmodule MyApp.Handler do\n  import Aino.Middleware.Routes, only: [get: 2, get: 3, post: 2]\n\n  @behaviour Aino.Handler\n\n  def routes() do\n    [\n      get(\"/\", \u0026Index.index/1, as: :root),\n      get(\"/about\", \u0026Index.about/1, as: :about),\n      order_routes()\n    ]\n  end\n\n  defp order_routes() do\n    [\n      get(\"/orders\", \u0026Orders.index/1, as: :orders),\n      get(\"/orders/:id\", \u0026Orders.show/1, as: :order),\n      post(\"/orders\", \u0026Orders.create/1)\n    ]\n  end\n\n  @impl true\n  def handle(token) do\n    middleware = [\n      Aino.Middleware.common(),\n      \u0026Aino.Middleware.Routes.routes(\u00261, routes()),\n      \u0026Aino.Middleware.Routes.match_route/1,\n      \u0026Aino.Middleware.params/1,\n      \u0026Aino.Middleware.Routes.handle_route/1,\n    ]\n\n    Aino.Token.reduce(token, middleware)\n  end\nend\n```\n\nThe 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.\n\n```elixir\ndefmodule Index do\n  alias Aino.Token\n\n  def index(token) do\n    token\n    |\u003e Token.response_status(200)\n    |\u003e Token.response_header(\"Content-Type\", \"text/html\")\n    |\u003e Token.response_body(Index.View.render(\"index.html\"))\n  end\nend\n\ndefmodule Index.View do\n  require Aino.View\n\n  Aino.View.compile [\n    \"lib/index/index.html.eex\"\n  ]\nend\n```\n\n## Concepts\n\n### `Aino.Handler`\n\nA handler processes an incoming request from Aino.\n\nThe `handle/1` function is passed an `Aino.Token`.\n\nThe handler _must_ return a token that contains three keys to return a response:\n\n- `:response_status`\n- `:response_headers`\n- `:response_body`\n\nIf the token does not contain these three keys, a 500 error is returned.\n\nInside your handler, you may wish to use several `Aino.Middleware` including\n`Aino.Middleware.common/0`.\n\n### `Aino.Token`\n\nThe token is what flows through the entire web request. Tokens are simple maps\nthat contain no defined keys beyond `:request`. Several Aino middleware add\nkeys and they are documented in the functions.\n\n\n### `Aino.Middleware`\n\nMiddleware are simple functions that take the token and return the token. They process\nthe request and add or modify existing keys on the token.\n\nAn example middleware is `Aino.Middleware.headers/1`:\n\n```elixir\ndef headers(%{request: request} = token) do\n  headers =\n    Enum.map(request.headers, fn {header, value} -\u003e\n      {String.downcase(header), value}\n    end)\n\n  Map.put(token, :headers, headers)\nend\n```\n\n[elli]: https://github.com/elli-lib/elli\n","funding_links":["https://github.com/sponsors/oestrich","https://patreon.com/ericoestrich"],"categories":["Elixir"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foestrich%2Faino","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foestrich%2Faino","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foestrich%2Faino/lists"}