{"id":13576382,"url":"https://github.com/ArangoDB-Community/arangox","last_synced_at":"2025-04-05T05:31:39.995Z","repository":{"id":51145063,"uuid":"207661352","full_name":"ArangoDB-Community/arangox","owner":"ArangoDB-Community","description":"ArangoDB 3.11 driver for Elixir with connection pooling, support for VelocyStream, active failover, transactions and streamed cursors.","archived":false,"fork":false,"pushed_at":"2024-04-22T17:52:19.000Z","size":150,"stargazers_count":53,"open_issues_count":0,"forks_count":8,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-03-30T18:34:40.222Z","etag":null,"topics":["arangodb-client","elixir","velocystream"],"latest_commit_sha":null,"homepage":"","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/ArangoDB-Community.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,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-09-10T20:54:58.000Z","updated_at":"2025-02-24T11:36:50.000Z","dependencies_parsed_at":"2024-04-22T19:07:25.999Z","dependency_job_id":"84aa163c-12ba-4075-b8fa-cc4af6446478","html_url":"https://github.com/ArangoDB-Community/arangox","commit_stats":{"total_commits":68,"total_committers":4,"mean_commits":17.0,"dds":0.6470588235294117,"last_synced_commit":"8ce80b965958a75523ce6f4c68faa8cc83c811d9"},"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArangoDB-Community%2Farangox","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArangoDB-Community%2Farangox/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArangoDB-Community%2Farangox/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ArangoDB-Community%2Farangox/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ArangoDB-Community","download_url":"https://codeload.github.com/ArangoDB-Community/arangox/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247294468,"owners_count":20915335,"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":["arangodb-client","elixir","velocystream"],"created_at":"2024-08-01T15:01:09.765Z","updated_at":"2025-04-05T05:31:34.983Z","avatar_url":"https://github.com/ArangoDB-Community.png","language":"Elixir","funding_links":[],"categories":["Elixir"],"sub_categories":[],"readme":"# Arangox\n\n[![Version](https://img.shields.io/hexpm/v/arangox.svg)](https://hex.pm/packages/arangox)\n[![CI](https://github.com/ArangoDB-Community/arangox/actions/workflows/elixir.yml/badge.svg?branch=main\u0026event=push)](https://github.com/ArangoDB-Community/arangox/actions/workflows/elixir.yml)\n\nAn implementation of [`DBConnection`](https://hex.pm/packages/db_connection) for\n[ArangoDB](https://www.arangodb.com).\n\nSupports [VelocyStream](https://www.arangodb.com/2017/08/velocystream-async-binary-protocol/),\n[active failover](https://www.arangodb.com/docs/stable/architecture-deployment-modes-active-failover-architecture.html),\ntransactions and streamed cursors.\n\nTested on:\n\n- **ArangoDB** 3.11\n- **Elixir** 1.16\n- **OTP** 26\n\n[HexDocs](https://hexdocs.pm/arangox/readme.html)\n\n## Examples\n\n```elixir\niex\u003e {:ok, conn} = Arangox.start_link(pool_size: 10)\niex\u003e {:ok, %Arangox.Response{status: 200, body: %{\"code\" =\u003e 200, \"error\" =\u003e false, \"mode\" =\u003e \"default\"}}} = Arangox.get(conn, \"/_admin/server/availability\")\niex\u003e {:error, %Arangox.Error{status: 404}} = Arangox.get(conn, \"/invalid\")\niex\u003e %Arangox.Response{status: 200, body: %{\"code\" =\u003e 200, \"error\" =\u003e false, \"mode\" =\u003e \"default\"}} = Arangox.get!(conn, \"/_admin/server/availability\")\niex\u003e {:ok,\niex\u003e   %Arangox.Request{\niex\u003e     body: \"\",\niex\u003e     headers: %{},\niex\u003e     method: :get,\niex\u003e     path: \"/_admin/server/availability\"\niex\u003e   },\niex\u003e   %Arangox.Response{\niex\u003e     status: 200,\niex\u003e     body: %{\"code\" =\u003e 200, \"error\" =\u003e false, \"mode\" =\u003e \"default\"}\niex\u003e   }\niex\u003e } = Arangox.request(conn, :get, \"/_admin/server/availability\")\niex\u003e Arangox.transaction(conn, fn c -\u003e\niex\u003e   stream =\niex\u003e     Arangox.cursor(\niex\u003e       c,\niex\u003e       \"FOR i IN [1, 2, 3] FILTER i == 1 || i == @num RETURN i\",\niex\u003e       %{num: 2},\niex\u003e       properties: [batchSize: 1]\niex\u003e     )\niex\u003e\niex\u003e   Enum.reduce(stream, [], fn resp, acc -\u003e\niex\u003e     acc ++ resp.body[\"result\"]\niex\u003e   end)\niex\u003e end)\n{:ok, [1, 2]}\n```\n\n## Clients\n\n### Velocy\n\nBy default, Arangox communicates with _ArangoDB_ via _VelocyStream_, which requires the `:velocy` library:\n\n```elixir\ndef deps do\n  [\n    ...\n    {:arangox, \"~\u003e 0.4.0\"},\n    {:velocy, \"~\u003e 0.1\"}\n  ]\nend\n```\n\nThe default vst chunk size is `30_720`. To change it, you can include the following in your `config/config.exs`:\n\n```elixir\nconfig :arangox, :vst_maxsize, 12_345\n```\n\n### HTTP\n\nArangox has two HTTP clients, `Arangox.GunClient` and `Arangox.MintClient`, they require a json library:\n\n```elixir\ndef deps do\n  [\n    ...\n    {:arangox, \"~\u003e 0.4.0\"},\n    {:jason, \"~\u003e 1.1\"},\n    {:gun, \"~\u003e 1.3.0\"} # or {:mint, \"~\u003e 0.4.0\"}\n  ]\nend\n```\n\n```elixir\nArangox.start_link(client: Arangox.GunClient) # or Arangox.MintClient\n```\n\n```elixir\niex\u003e {:ok, conn} = Arangox.start_link(client: Arangox.GunClient)\niex\u003e {:ok, %Arangox.Response{status: 200, body: nil}} = Arangox.options(conn, \"/\")\n```\n\n**NOTE:** `:mint` doesn't support unix sockets.\n\n**NOTE:** Since `:gun` is an Erlang library, you _might_ need to add it as an extra application in `mix.exs`:\n\n```elixir\ndef application() do\n  [\n    extra_applications: [:logger, :gun]\n  ]\nend\n```\n\nTo use something else, you'd have to implement the `Arangox.Client` behaviour in a\nmodule somewhere and set that instead.\n\nThe default json library is `Jason`. To use a different library, set the `:json_library` config to the module of your choice, i.e:\n\n```elixir\nconfig :arangox, :json_library, Poison\n```\n\n### Benchmarks\n\n**pool size** 10  \n**parallel processes** 1000  \n**system** virtual machine, 1 cpu (not shared), 2GB RAM\n\n| Name         | Latency   |\n| ------------ | --------- |\n| Velocy: GET  | 179.74 ms |\n| Velocy: POST | 201.23 ms |\n| Mint: GET    | 207.00 ms |\n| Mint: POST   | 216.53 ms |\n| Gun: GET     | 222.61 ms |\n| Gun: POST    | 243.65 ms |\n\n\u003csub\u003eResults generated with [`Benchee`](https://hex.pm/packages/benchee).\u003c/sub\u003e\n\n## Start Options\n\nArangox assumes defaults for the `:endpoints`, `:username` and `:password` options,\nand [`db_connection`](https://hex.pm/packages/db_connection) assumes a default\n`:pool_size` of `1`, so the following:\n\n```elixir\nArangox.start_link()\n```\n\nIs equivalent to:\n\n```elixir\noptions = [\n  endpoints: \"http://localhost:8529\",\n  pool_size: 1\n]\nArangox.start_link(options)\n```\n\n## Endpoints\n\nUnencrypted endpoints can be specified with either `http://` or\n`tcp://`, whereas encrypted endpoints can be specified with `https://`,\n`ssl://` or `tls://`:\n\n```elixir\n\"tcp://localhost:8529\" == \"http://localhost:8529\"\n\"https://localhost:8529\" == \"ssl://localhost:8529\" == \"tls://localhost:8529\"\n\n\"tcp+unix:///tmp/arangodb.sock\" == \"http+unix:///tmp/arangodb.sock\"\n\"https+unix:///tmp/arangodb.sock\" == \"ssl+unix:///tmp/arangodb.sock\" == \"tls+unix:///tmp/arangodb.sock\"\n\n\"tcp://unix:/tmp/arangodb.sock\" == \"http://unix:/tmp/arangodb.sock\"\n\"https://unix:/tmp/arangodb.sock\" == \"ssl://unix:/tmp/arangodb.sock\" == \"tls://unix:/tmp/arangodb.sock\"\n```\n\nThe `:endpoints` option accepts either a binary, or a list of binaries. In the case of a list,\nArangox will try to establish a connection with the first endpoint it can.\n\nIf a connection is established, the availability of the server will be checked (via the _ArangoDB_ api), and\nif an endpoint is in maintenance mode or is a _Follower_ in an _Active Failover_ setup, the connection\nwill be dropped, or in the case of a list, the endpoint skipped.\n\nWith the `:read_only?` option set to `true`, arangox will try to find a server in\n_readonly_ mode instead and add the _x-arango-allow-dirty-read_ header to every request:\n\n```elixir\niex\u003e endpoints = [\"http://localhost:8003\", \"http://localhost:8004\", \"http://localhost:8005\"]\niex\u003e {:ok, conn} = Arangox.start_link(endpoints: endpoints, read_only?: true)\niex\u003e %Arangox.Response{body: body} = Arangox.get!(conn, \"/_admin/server/mode\")\niex\u003e body[\"mode\"]\n\"readonly\"\niex\u003e {:error, %Arangox.Error{status: 403}} = Arangox.post(conn, \"/_api/database\", %{name: \"newDatabase\"})\n```\n\n## Authentication\n\n### Velocy\n\nArangoDB's VelocyStream endpoints _do not_ read authorization headers, authentication configuration _must_ be \nprovided as options to `Arangox.start_link/1`. \n\nAs a consequence, if you're using bearer auth, there are a couple of caveats to bear in mind:\n\n* New JWT tokens can only be requested in a seperate connection (i.e. during startup before the primary pool\nis initialized)\n* Refreshed tokens can only be authorized by restarting a connection pool\n\n### HTTP\n\nWhen using an HTTP client, Arangox will generate a _Basic_ or _Bearer_ authorization header if the `:auth` option is set to `{:basic, username, password}` or to `{:bearer, token}` respectively, and append it to every request. If the `:auth` option is not explicitly set, no authorization header will be appended.\n\n```elixir\niex\u003e {:ok, conn} = Arangox.start_link(client: Arangox.GunClient, endpoints: \"http://localhost:8001\")\niex\u003e {:error, %Arangox.Error{status: 401}} = Arangox.get(conn, \"/_admin/server/mode\")\n```\n\nThe header value is obfuscated in transfomed requests returned by arangox, for obvious reasons:\n\n```elixir\niex\u003e {:ok, conn} = Arangox.start_link(client: Arangox.GunClient, auth: {:basic, \"root\", \"\"})\niex\u003e {:ok, request, _response} = Arangox.request(conn, :options, \"/\")\niex\u003e request.headers\n%{\"authorization\" =\u003e \"...\"}\n```\n\n## Databases\n\n### Velocy\n\nIf the `:database` option is set, it can be overridden by prepending the path of a\nrequest with `/_db/:value`. If nothing is set, the request will be sent as-is and\n_ArangoDB_ will assume the `_system` database.\n\n### HTTP\n\nWhen using an HTTP client, arangox will prepend `/_db/:value` to the path of every request\nonly if one isn't already prepended. If a `:database` option is not set, nothing is prepended.\n\n```elixir\niex\u003e {:ok, conn} = Arangox.start_link(client: Arangox.GunClient)\niex\u003e {:ok, request, _response} = Arangox.request(conn, :get, \"/_admin/time\")\niex\u003e request.path\n\"/_admin/time\"\niex\u003e {:ok, conn} = Arangox.start_link(database: \"_system\", client: Arangox.GunClient)\niex\u003e {:ok, request, _response} = Arangox.request(conn, :get, \"/_admin/time\")\niex\u003e request.path\n\"/_db/_system/_admin/time\"\niex\u003e {:ok, request, _response} = Arangox.request(conn, :get, \"/_db/_system/_admin/time\")\niex\u003e request.path\n\"/_db/_system/_admin/time\"\n```\n\n## Headers\n\nHeaders can be given as maps:\n\n```elixir\n%{\"header\" =\u003e \"value\"}\n```\n\nOr lists of two binary element tuples:\n\n```elixir\n[{\"header\", \"value\"}]\n```\n\nHeaders given to the start option are merged with every request, but will not override\nany of the headers set by Arangox:\n\n```elixir\niex\u003e {:ok, conn} = Arangox.start_link(headers: %{\"header\" =\u003e \"value\"})\niex\u003e {:ok, request, _response} = Arangox.request(conn, :get, \"/_api/version\")\niex\u003e request.headers\n%{\"header\" =\u003e \"value\"}\n```\n\nHeaders passed to requests will override any of the headers given to the start option\nor set by Arangox:\n\n```elixir\niex\u003e {:ok, conn} = Arangox.start_link(headers: %{\"header\" =\u003e \"value\"})\niex\u003e {:ok, request, _response} = Arangox.request(conn, :get, \"/_api/version\", \"\", %{\"header\" =\u003e \"new_value\"})\niex\u003e request.headers\n%{\"header\" =\u003e \"new_value\"}\n```\n\n## Transport\n\nThe `:connect_timeout` start option defaults to `5_000`.\n\nTransport options can be specified via `:tcp_opts` and `:ssl_opts`, for unencrypted and\nencrypted connections respectively. When using `:gun` or `:mint`, these options are passed\ndirectly to the `:transport_opts` connect option.\n\nSee [`:gen_tcp.connect_option()`](http://erlang.org/doc/man/gen_tcp.html#type-connect_option)\nfor more information on `:tcp_opts`,\nor [`:ssl.tls_client_option()`](http://erlang.org/doc/man/ssl.html#type-tls_client_option) for `:ssl_opts`.\n\nThe `:client_opts` option can be used to pass client-specific options to `:gun` or `:mint`.\nThese options are merged with and may override values set by arangox. Some options cannot be\noverridden (i.e. `:mint`'s `:mode` option). If `:transport_opts` is set here it will override\neverything given to `:tcp_opts` or `:ssl_opts`, regardless of whether or not a connection is\nencrypted.\n\nSee the `gun:opts()` type in the [gun docs](https://ninenines.eu/docs/en/gun/1.3/manual/gun/)\nor [`connect/4`](https://hexdocs.pm/mint/Mint.HTTP.html#connect/4) in the mint docs for more\ninformation.\n\n## Request Options\n\nRequest options are handled by and passed directly to `:db_connection`.\nSee [execute/4](https://hexdocs.pm/db_connection/DBConnection.html#execute/4) in the `:db_connection` docs for supported\noptions.\n\nRequest timeouts default to `15_000`.\n\n```elixir\niex\u003e {:ok, conn} = Arangox.start_link()\niex\u003e %Arangox.Response{status: 200, body: %{\"code\" =\u003e 200, \"error\" =\u003e false, \"mode\" =\u003e \"default\"}} = Arangox.get!(conn, \"/_admin/server/availability\", [], timeout: 15_000)\n```\n\n## Contributing\n\n```\nmix format\nmix do format, credo --strict\ndocker-compose up -d\nmix test\n```\n\n## Roadmap\n\n- `:get_endpoints` and `:port_mappings` options\n- An Ecto adapter\n- More descriptive logs\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FArangoDB-Community%2Farangox","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FArangoDB-Community%2Farangox","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FArangoDB-Community%2Farangox/lists"}