Arex

Arex is an ArcadeDB-native Elixir client for applications that want a direct, idiomatic API over ArcadeDB's HTTP interface.

It is built around a small set of promises:

Highlights

Documentation Map

Installation

Add Arex to your dependencies:

defp deps do
  [
    {:arex, "~> 0.1.0"}
  ]
end

Then fetch dependencies:

mix deps.get

Configuration

Arex resolves connection settings in this order:

  1. per-call options
  2. application config
  3. environment variables for url, user, pwd, and db

language works a little differently. It is resolved from call options or application config and otherwise defaults to "sql".

Recommended runtime.exs configuration:

import Config

config :arex,
  url: System.fetch_env!("ARCADEDB_URL"),
  user: System.fetch_env!("ARCADEDB_USER"),
  pwd: System.fetch_env!("ARCADEDB_PASSWORD"),
  db: System.fetch_env!("ARCADEDB_DATABASE"),
  language: "sql"

If you want Arex's built-in environment fallback, export these variables:

Common call options:

Quick Start

The following example assumes you have a running ArcadeDB server and want to store tenant-scoped customer records in a crm database.

alias Arex.{Query, Record, Schema}

# Run this once when bootstrapping an empty database.
{:ok, _} = Schema.create_document_type("Customer", db: "crm")
{:ok, _} = Schema.create_property("Customer", "external_id", :string, db: "crm")
{:ok, _} = Schema.create_index("Customer", ["external_id"], db: "crm", unique: true)

{:ok, customer} =
  Record.persist(
    %{external_id: "cust-1", name: "Ada Lovelace"},
    db: "crm",
    type: "Customer",
    tenant: "ankara",
    scope: "sales"
  )

{:ok, same_customer} =
  Record.fetch(customer["@rid"], db: "crm", tenant: "ankara", scope: "sales")

{:ok, page} =
  Query.page(
    "select from Customer where tenant = :tenant and scope = :scope order by @rid",
    %{"tenant" => "ankara", "scope" => "sales"},
    db: "crm",
    limit: 25
  )

Boundary Model

Arex treats db, tenant, and scope as separate layers of isolation.

Boundary rules are consistent across the high-level APIs:

Raw escape hatches such as Arex.KV.run/2, Arex.KV.batch/2, and hand-written TimeSeries SQL or PromQL remain caller-controlled.

This gives application code a stable model without repeating the same predicates in every call site.

Module Guide

Module Use it for
Arex connectivity checks and server metadata
Arex.Query raw reads, paging, and streaming
Arex.Command raw write commands and SQLScript execution
Arex.Record document-style CRUD helpers
Arex.Schema types, properties, indexes, and buckets
Arex.Database database creation, existence checks, and summaries
Arex.KV Redis-style key/value and hash helpers
Arex.TimeSeries TimeSeries DDL, ingestion, and query endpoints
Arex.Vector dense, sparse, and hybrid vector search helpers
Arex.Vertex vertex creation, updates, and traversal
Arex.Edge edge creation and lookups between vertices
Arex.Error normalized error maps returned by all helpers

Common Workflows

Records And Queries

Use Arex.Record when you want the library to handle type resolution, boundary stamping, and common CRUD patterns for you.

{:ok, customer} =
  Arex.Record.upsert(
    "Customer",
    %{name: "Ada Lovelace", status: "active"},
    db: "crm",
    where: %{external_id: "cust-1"},
    tenant: "ankara",
    scope: "sales"
  )

{:ok, exists?} =
  Arex.Record.is_there?(
    %{external_id: "cust-1"},
    db: "crm",
    type: "Customer",
    tenant: "ankara",
    scope: "sales"
  )

{:ok, first_row} =
  Arex.Query.first(
    "select from Customer where tenant = :tenant and scope = :scope order by @rid",
    %{"tenant" => "ankara", "scope" => "sales"},
    db: "crm"
  )

Use Arex.Query or Arex.Command when you need explicit control over the statement you send to ArcadeDB.

Graph Workflows

Use Arex.Vertex and Arex.Edge when your application works with graph types but you still want the same boundary semantics as document helpers.

{:ok, alice} =
  Arex.Vertex.create(
    "Person",
    %{name: "Alice"},
    db: "social",
    tenant: "ankara",
    scope: "graph"
  )

{:ok, bob} =
  Arex.Vertex.create(
    "Person",
    %{name: "Bob"},
    db: "social",
    tenant: "ankara",
    scope: "graph"
  )

{:ok, _edge} =
  Arex.Edge.create(
    "Knows",
    alice["@rid"],
    bob["@rid"],
    %{},
    db: "social",
    tenant: "ankara",
    scope: "graph"
  )

{:ok, neighbors} =
  Arex.Vertex.out(alice["@rid"], "Knows", db: "social", tenant: "ankara", scope: "graph")

Schema And Database Administration

Use Arex.Database and Arex.Schema for setup, migrations, test provisioning, and operational inspection.

{:ok, :created} = Arex.Database.create("analytics")
{:ok, _} = Arex.Schema.create_document_type("Event", db: "analytics")
{:ok, _} = Arex.Schema.create_property("Event", "kind", :string, db: "analytics")
{:ok, _} = Arex.Schema.create_index("Event", ["kind"], db: "analytics")
{:ok, stats} = Arex.Database.stats(db: "analytics")

For a deeper walkthrough, see Graph and Schema.

Key/Value Workflows

Use Arex.KV when you want Redis-style helpers over ArcadeDB's Redis-language command surface without constructing raw command strings.

{:ok, "OK"} =
  Arex.KV.set(
    "session:ada",
    "online",
    db: "crm",
    tenant: "ankara",
    scope: "sales"
  )

{:ok, "online"} =
  Arex.KV.get(
    "session:ada",
    db: "crm",
    tenant: "ankara",
    scope: "sales"
  )

Wrapped key helpers namespace keys by active tenant and scope. Raw helpers such as Arex.KV.run/2 and Arex.KV.batch/2 stay raw and do not rewrite arbitrary Redis command strings.

Time-Series Workflows

Use Arex.TimeSeries when you want TimeSeries DDL, ingestion, and endpoint helpers without hand-building /ts requests or raw create timeseries type statements.

{:ok, _} =
  Arex.TimeSeries.create_type(
    "CpuMetric",
    "ts",
    [{"host", :string}],
    [{"value", :double}],
    db: "metrics",
    tenant: "ankara",
    scope: "ops"
  )

{:ok, _} =
  Arex.TimeSeries.insert(
    "CpuMetric",
    %{"ts" => 1_715_000_001_000, "host" => "app-1", "value" => 0.42},
    db: "metrics",
    tenant: "ankara",
    scope: "ops"
  )

{:ok, rows} =
  Arex.TimeSeries.query_sql(
    "select from CpuMetric where host = :host order by ts desc",
    %{"host" => "app-1"},
    db: "metrics",
    tenant: "ankara",
    scope: "ops"
  )

Structured TimeSeries helpers stamp tenant and scope as tags when present. Raw SQL, PromQL, and raw payload endpoints remain available when you need full control over the underlying ArcadeDB surface.

Vector Workflows

Use Arex.Vector when you want a typed wrapper around ArcadeDB vector indexes and nearest-neighbor queries.

{:ok, _} = Arex.Schema.create_document_type("Doc", db: "search")
{:ok, _} = Arex.Vector.create_embedding_property("Doc", "embedding", db: "search")

{:ok, _} =
  Arex.Vector.create_dense_index(
    "Doc",
    "embedding",
    768,
    db: "search",
    similarity: :cosine
  )

{:ok, neighbors} =
  Arex.Vector.neighbors(
    "Doc[embedding]",
    [0.12, 0.34, 0.56],
    10,
    db: "search"
  )

The wrapper does not hide ArcadeDB vector concepts. It exists to make common index metadata and query construction easier to read and harder to get wrong.

Return Values And Errors

All public helpers return one of two shapes:

Example error:

{:error,
 %{
   kind: :not_found,
   message: "record not found",
   status: nil,
   arcade_code: nil,
   details: nil,
   body: %{},
   request: %{method: :post, path: "/api/v1/query/mydb"}
 }}

Important contract notes:

Runtime Behavior

Arex exposes a small, explicit set of transport controls:

If you need the lower-level details, see Runtime Behavior.

ArcadeDB Compatibility Notes

Arex documents the ArcadeDB quirks it depends on rather than hiding them:

Observability

Arex does not emit its own Telemetry events or logs. Instrumentation belongs at the application boundary:

Local Development

The integration suite expects a live ArcadeDB server with an empty test_db database and the test_user account available. The official Docker image can provision that in one command:

docker run --rm -p 2480:2480 -p 2424:2424 \
  -e 'JAVA_OPTS=-Darcadedb.server.rootPassword=root_password -Darcadedb.server.defaultDatabases=test_db[test_user:test_password]' \
  arcadedata/arcadedb:latest

With the server running, export the values expected by local docs generation and the integration tests:

export AREX_URL=http://localhost:2480/
export AREX_USER=test_user
export AREX_PWD=test_password
export AREX_DB=test_db

Typical maintenance flow:

mix format
mix docs
mix test --cover

mix docs writes the generated site to doc/.

Additional Reading