{"id":13722300,"url":"https://github.com/tsujigiri/axiom","last_synced_at":"2026-01-11T00:53:54.987Z","repository":{"id":3969368,"uuid":"5063831","full_name":"tsujigiri/axiom","owner":"tsujigiri","description":"a micro-framework for web applications in Erlang","archived":false,"fork":false,"pushed_at":"2023-02-16T10:09:03.000Z","size":127,"stargazers_count":268,"open_issues_count":7,"forks_count":28,"subscribers_count":25,"default_branch":"master","last_synced_at":"2024-11-14T11:39:59.839Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Erlang","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/tsujigiri.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2012-07-16T06:37:10.000Z","updated_at":"2024-11-06T04:16:34.000Z","dependencies_parsed_at":"2023-07-05T17:00:53.069Z","dependency_job_id":null,"html_url":"https://github.com/tsujigiri/axiom","commit_stats":{"total_commits":136,"total_committers":7,"mean_commits":"19.428571428571427","dds":0.07352941176470584,"last_synced_commit":"a3cfeec81fa44adabb3da2c05e355782e2cbf1ce"},"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsujigiri%2Faxiom","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsujigiri%2Faxiom/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsujigiri%2Faxiom/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsujigiri%2Faxiom/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tsujigiri","download_url":"https://codeload.github.com/tsujigiri/axiom/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252905515,"owners_count":21822818,"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":[],"created_at":"2024-08-03T01:01:27.070Z","updated_at":"2025-05-07T15:30:31.901Z","avatar_url":"https://github.com/tsujigiri.png","language":"Erlang","funding_links":[],"categories":["General Libraries","Erlang","Web Frameworks"],"sub_categories":[],"readme":"# Axiom [![Build Status](https://secure.travis-ci.org/tsujigiri/axiom.png?branch=master)](http://travis-ci.org/tsujigiri/axiom)\n\nAxiom is a micro-framework for building web applications in Erlang.\nIt is inspired by [Sinatra](http://sinatrarb.com) and built on top of\n[Cowboy](https://github.com/extend/cowboy).\n\n## Getting Started\n\nAxiom is built to make creating web applications fast and easy.\nA minimal application would look like this:\n\n```erlang\n-module(my_app).\n-export([start/0, handle/3]).\n\nstart() -\u003e\n\taxiom:start(?MODULE).\n\nhandle(\u003c\u003c\"GET\"\u003e\u003e, [\u003c\u003c\"hi\"\u003e\u003e], _Request) -\u003e\n\t\u003c\u003c\"Hello world!\"\u003e\u003e.\n\n```\n\nThis handles requests for `GET /hi` and returns \"Hello world!\".\n\nThe third argument given to the handler is of type `cowboy_req:req()`. Use the\n`cowboy_req` module, if you need anything out of the request.\n\nThe return value can be a binary string or iolist. So, this also works:\n\n```erlang\nhandle(\u003c\u003c\"GET\"\u003e\u003e, [\u003c\u003c\"hello\"\u003e\u003e, Who], _Request) -\u003e\n\t[\u003c\u003c\"Hello \"\u003e\u003e, Who, \u003c\u003c\"!\"\u003e\u003e].\n```\n\nIf you want to specify a response status code and/or headers, use a tuple with\neither the status code and body or status code, headers and body, in these\nrespective orders.\n\nExamples:\n\n```erlang\n{418, \u003c\u003c\"\u003ch1\u003eI'm a teapot!\u003c/h1\u003e\"\u003e\u003e}\n```\nor\n```erlang\n{418, [{\u003c\u003c\"X-Foo\"\u003e\u003e, \u003c\u003c\"bar\"\u003e\u003e}], \u003c\u003c\"\u003ch1\u003eI'm a teapot!\u003c/h1\u003e\"\u003e\u003e}\n```\n\nAs a third option a `cowboy_req:req()` can be returned. In this case, to set the\nresponse headers and body, use the `cowboy_req:set_resp_header/3` and\n`cowboy_req:set_resp_body/2` functions. To set the status code, use\n`axiom:set_resp_status/2`. These functions return a new `cowboy_req:req()` to be\nused further and to be returned from `YourHandler:handle/3`.\n\nThe full spec of `YourHandler:handle/3` is expected to look like this:\n\n```erlang\nhandle(Method, Path, Req) -\u003e Body | Req | {Status, Body} | {Status, Headers, Body}.\n\n  Types:\n    Method = binary(),\n    Path = [PathSegment]\n    PathSegment = binary()\n    Req = cowboy_req:req()\n    Body = iodata()\n    Status = non_neg_integer()\n    Headers = [Header]\n    Header = {binary(), binary()}\n```\n\n## Request parameters\n\nTo get the request parameters out of the request, you can use the two\nhandy functions `axiom:params(Req)` and `axiom:param(Name, Req)`.\nThe first returns a proplist of all parameters, the second one returns\nthe named parameter's value. Keys and values are binary strings.\n\n## Configuration\n\n`axiom:start/1` has a bigger brother called `axiom:start/2`, taking a\nproplist as the second argument. Possible properties and their defaults\nare as follows:\n\n```erlang\n[\n\t{nb_acceptors: 100},\t\t% acceptor pool size\n\t{host, '_'},\t\t\t\t% host IP\n\t{port, 7654},\t\t\t\t% host port\n\t{public, \"public\"}\t\t\t% custom path for static files\n]\n```\n\n## Static Files\n\nStatic files are served via the `cowboy_static` handler. By default, every file\nin the ./public directory and all its subdirectories will be made accessible via\nURL path the same as file's relative path. E.g. the file `./public/about.html`\ncan be accessed via `GET /about.html`. **Note**: Currently, if the contents of\nthe ./public subtree change, Axiom needs to be restarted to reflect the change.\n\nYou can specify a custom directory via the `public` option.\n\nWhen you use this feature, it is advisable to start Erlang with the\n`+A n` flag. This will start `n` async threads.\nRule of thumb is to use your machine's number of CPU cores.\n\n\n## Redirects\n\nYou can redirect requests with `redirect/2`:\n\n```erlang\nhandle(\u003c\u003c\"GET\"\u003e\u003e, [\u003c\u003c\"foo\"\u003e\u003e], Req) -\u003e\n  Req1 = axiom:redirect(\"/bar\", Req),\n  Req;\n\nhandle(\u003c\u003c\"GET\"\u003e\u003e, [\u003c\u003c\"bar\"\u003e\u003e], Request) -\u003e\n\t\u003c\u003c\"\u003ch1\u003eWelcome back!\u003c/h1\u003e\"\u003e\u003e.\n```\n\n## Templates\n\nAxiom comes with [Django](https://github.com/django/django) template\nsupport via [erlydtl](https://github.com/evanmiller/erlydtl). To make\nuse of it in your application, create a directory named `templates` and\nin it, create a template, e.g. `my_template.dtl`:\n\n```dtl\n\u003ch1\u003eHello {{who}}\u003c/h1\u003e\n```\n\nIn your handler, specify the template to be rendered:\n\n```erlang\nhandle(\u003c\u003c\"GET\"\u003e\u003e, [\u003c\u003c\"hello\"\u003e\u003e], _Request) -\u003e\n\taxiom:dtl(my_template, [{who, \"you\"}]).\n```\n\nFor convenience, the second argument, a proplist of parameters, can have\natoms, lists or binaries as keys. That way request parameters can be put\nin there, without you having to convert them first.\n\nThe templates are compiled into modules when `rebar compile` is\ncalled.\n\nTo see what else erlydtl can do for you, take a look at\n[its project page](https://github.com/erlydtl/erlydtl).\n\n\n## Sessions\n\nAxiom comes with a basic session handler and ets based session store. To\nuse it, add this tuple to the configuration proplist:\n\n```erlang\n{sessions, []}\n```\n\nIn your handler you can then use\n`axiom_session:set(Key, Value, Request)` and\n`axiom_session:get(Key, Request)`.\n\nTo set attributes for the cookie, storing the session ID, add some\nparameters to the session configuration in a tuple with the key\n`cookies`:\n\n```erlang\n{sessions, [{cookies, [param()]}]}\n```\n\nPossible parameters are:\n\n```erlang\nparam() = {max_age, integer()} |\n\t\t  {local_time, calendar:datetime()} |\n\t\t  {domain, binary()} |\n\t\t  {path, binary()} |\n\t\t  {secure, true | false} |\n\t\t  {http_only, true | false}\n```\n\nThe default session store is the `axiom_session_ets` module. You can use\nyour own by adding a `store` tuple to the sessions tuple:\n\n```erlang\n{sessions, [{store, my_session_store, []}]}\n```\n\nFor implementation details take a look into the `axiom_session_ets`\nmodule.\n\n## Filters\n\nThe functions `before_filter/1` and `after_filter/1` can be implemented\nto deal with the `cowboy_req:req()` before and after `YourHandler:handle/3`.\nWhen implemented, these are called no matter which `handle` function matches the\nrequest.\n\nIn your handler module:\n\n```erlang\nbefore_filter(Req) -\u003e\n\t% do stuff\n\tReq.\n\nafter_filter(Req) -\u003e\n\t% do more stuff\n\tReq.\n```\n\n## Errors\n\n### Not Found\n\nTo overwrite Axiom's response to 404 errors, just create a catch-all\nhandler:\n\n```erlang\nhandle(_Method, _Path, _Req) -\u003e\n\t{404, \u003c\u003c\"nope.\"\u003e\u003e}.\n```\n\nNote that you have to take care of the status code yourself, as\notherwise the default of 200 is sent back to the client.\n\n### Internal Server Error\n\nTo handle these yourself, you can implement a function named `error/1`.\nThe argument is the `cowboy_req:req()` object, otherwise it works like your\n`Handler:handle/3` function.\n\n## Streaming\n\nTo send a chunked reply, call `axiom:chunk/2` for each chunk:\n\n```erlang\nchunk(Data::iodata(), Req::cowboy_req:req()) -\u003e {ok, Req1::cowboy_req:req()}.\n```\n\nThe returned `cowboy_req:req()` object has to be given as an argument to\nsubsequent calls to `chunk` and as the return value of your\n`Handler:handle/3` function.\n\nTo stream data with a Content-Type other than `text/html`, use\n`chunk/3`, which has an additional parameter, to be set to the type you\nwant:\n\n```erlang\nchunk(Data::iodata(), Req::cowboy_req:req(), Type::binary()) -\u003e {ok, Req1::cowboy_req:req()}.\n```\n\n### Example\n\n```erlang\nhandle(\u003c\u003c\"GET\"\u003e\u003e, [\u003c\u003c\"stream\"\u003e\u003e], Req) -\u003e\n\t{ok, Req1} = axiom:chunk(\u003c\u003c\"Hello\"\u003e\u003e, Req, \u003c\u003c\"text/plain\"\u003e\u003e),\n\t{ok, Req2} = axiom:chunk(\u003c\u003c\" world\"\u003e\u003e, Req1),\n\t{ok, Req3} = axiom:chunk(\u003c\u003c\"!\"\u003e\u003e, Req2),\n\tReq3.\n```\n\n## Installation\n\nTo use it in your OTP application, add this to your `rebar.config`:\n\n```erlang\n{lib_dirs, [\"deps\"]}.\n{deps, [\n\t{'axiom', \"0.1.0\", {git, \"git://github.com/tsujigiri/axiom.git\", {tag, \"v0.1.0\"}}}\n]}.\n```\n\nthen, as usual:\n\n```\nrebar get-deps\nrebar compile\n```\n\n## License\n\nPlease take a look at the `LICENSE` file! (tl;dr: it's the MIT License)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftsujigiri%2Faxiom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftsujigiri%2Faxiom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftsujigiri%2Faxiom/lists"}