This guide takes you from a running ArcadeDB server to your first successful reads and writes with Arex.
Arex stays small by design. You do not create a public client struct or open a session object in normal usage. Instead, you call module functions and pass options when you need to override defaults.
Prerequisites
You need:
- an ArcadeDB server that is reachable over HTTP
- credentials for that server
- a target database, or permission to create one
If you want a predictable local environment, the Docker command in README.md starts ArcadeDB with an empty test_db database and test credentials.
Install The Library
Add Arex to your dependency list:
defp deps do
[
{:arex, "~> 0.1.0"}
]
endThen fetch dependencies:
mix deps.get
Configure Connection Defaults
Arex resolves connection settings in this order:
- per-call options
- application config
- environment variables for
url,user,pwd, anddb
language is resolved from call options or application config and otherwise defaults to "sql".
Example runtime.exs:
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"Environment fallback names:
AREX_URLAREX_USERAREX_PWDAREX_DB
Verify Connectivity
Before writing application code, confirm that Arex can reach the server:
{:ok, :pong} = Arex.ping()
{:ok, info} = Arex.server_info()
{:ok, dbs} = Arex.Database.list()If you need to target a specific server without changing app config, pass connection options directly:
Arex.ping(
url: "http://localhost:2480/",
user: "test_user",
pwd: "test_password"
)Read Existing Data
When a database already contains data, Arex.Query is the fastest way to get started. With the empty local test database, create a type first and then query it:
{:ok, _} = Arex.Schema.create_document_type("Beer", db: "test_db")
{:ok, _} = Arex.Record.persist(%{id: 1, name: "Hocus Pocus"}, db: "test_db", type: "Beer")
{:ok, rows} =
Arex.Query.sql(
"select from Beer where id = :id",
%{"id" => 1},
db: "test_db"
)Helpful read helpers:
Arex.Query.sql/3executes SQL and returns all rows.Arex.Query.first/3returns the first row ornil.Arex.Query.one/3returns exactly one row or fails with:multiple_results.Arex.Query.page/3returns a page map withentries,limit,offset,count, andhas_more?.Arex.Query.stream_pages/3yields pages as a stream for larger result sets.
Create Your Own Database And Type
If you are starting from an empty environment, provision a database and type first:
{:ok, :created} = Arex.Database.create("crm")
{:ok, _} = Arex.Schema.create_document_type("Customer", db: "crm")
{:ok, _} = Arex.Schema.create_property("Customer", "external_id", :string, db: "crm")
{:ok, _} = Arex.Schema.create_index("Customer", ["external_id"], db: "crm", unique: true)That gives you a concrete record type for the higher-level CRUD helpers.
Write Your First Record
Arex.Record.persist/2 inserts when no @rid is present and updates when @rid is present.
{:ok, customer} =
Arex.Record.persist(
%{external_id: "cust-1", name: "Ada Lovelace"},
db: "crm",
type: "Customer",
tenant: "ankara",
scope: "sales"
)Important rules:
typeis required for inserts unless the input map already contains@typescopealways requirestenant- insert-like helpers stamp
tenantandscopeinto the record when present - reads using the same boundary only see records that match that boundary
Fetch the record again by RID:
{:ok, same_customer} =
Arex.Record.fetch(
customer["@rid"],
db: "crm",
tenant: "ankara",
scope: "sales"
)Understand The Boundary Model
Arex uses three layers of isolation:
dbpicks the ArcadeDB database.tenantscopes records inside that database.scopenarrows records inside the tenant.
Boundary-aware helpers use these rules consistently:
- writes stamp
tenantandscopeinto stored content - reads automatically filter by those fields when provided
- attempting to cross boundaries yields
:not_found - helper APIs reject direct mutation of protected boundary keys
This is one of Arex's main advantages over scattering raw SQL through application code.
Pick The Right Module
| Module | Start here when you need |
|---|---|
Arex | connectivity checks and server metadata |
Arex.Query | raw reads and paging |
Arex.Command | raw write commands or SQLScript |
Arex.Record | document-style CRUD |
Arex.Schema | types, properties, indexes, and buckets |
Arex.Database | create, drop, list, or inspect databases |
Arex.KV | Redis-style key/value and hash commands |
Arex.TimeSeries | TimeSeries DDL and endpoint wrappers |
Arex.Vector | vector properties, indexes, and search |
Arex.Vertex | graph vertex creation and traversal |
Arex.Edge | graph edge creation and lookup |
Specialized Models
Once the basic record flow is clear, the specialized modules follow the same
core Arex rules: option resolution, normalized {:ok, value} and
{:error, error_map} tuples, and explicit control over when you drop to raw
statements or raw HTTP.
Key/Value
Arex.KV wraps ArcadeDB's Redis-language command surface with boundary-aware
key helpers.
{: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"
)Important behavior:
- wrapped key helpers namespace keys by
tenantandscope - raw
run/2andbatch/2stay caller-controlled scopestill requirestenant
Time-Series
Arex.TimeSeries covers type creation, helper-managed inserts, and dedicated
TimeSeries endpoints.
{: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"
)Important behavior:
- helper-managed writes stamp
tenantandscopeas tags when present - wrapped SQL and latest-point reads apply those tags automatically
- raw SQL, raw PromQL, and raw payload helpers stay available when you need full control
Vector Search
Arex.Vector is the convenience layer for ArcadeDB vector properties, 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")This module keeps the public API close to ArcadeDB's vector features while avoiding repetitive metadata JSON and index SQL.
What To Read Next
- Records and Queries for the high-level CRUD API and paging semantics.
- Graph and Schema for schema changes, graph helpers, and provisioning workflows.
- Runtime Behavior for retries, timeouts, and normalized error handling.