A driver for TypeDB 3.12 and newer, built on the TypeDB HTTP API.
Older 3.x is not supported; see the README for what breaks and why.
Getting started
Add the connection to your supervision tree:
children = [
{TypeDB,
url: "http://localhost:8000",
username: "admin",
password: System.fetch_env!("TYPEDB_PASSWORD")}
]
Supervisor.start_link(children, strategy: :one_for_one)Then query. TypeDB is the default connection name:
TypeDB.query!(TypeDB, "social", """
match
$p isa person, has name $name;
select $name;
""")
|> Enum.map(&TypeDB.ConceptRow.typed_value(&1, "name"))
#=> ["Alice", "Bob"]The two ways to run a query
query/4 runs a single query in its own transaction — TypeDB opens it, runs
the query, and commits or closes it in one round trip. Reads never commit;
writes and schema changes commit by default, which commit: false turns off.
Every function takes the connection's registered name as its first argument;
conn below is that name, TypeDB unless you chose another.
TypeDB.query(conn, "social", "insert $p isa person, has name 'Alice';")transaction/5 opens a transaction you can run several queries in, committing
once at the end:
TypeDB.transaction(conn, "social", :write, fn tx ->
TypeDB.Transaction.query!(tx, "insert $p isa person, has name 'Alice';")
TypeDB.Transaction.query!(tx, "insert $p isa person, has name 'Bob';")
end)The block commits on success and rolls back on error or exception. A :read
block never commits.
Answers
Every query returns a TypeDB.Answer — see that module for the three shapes
and how to consume them.
Errors
Every function in this module and in TypeDB.Database, TypeDB.User,
TypeDB.Server and TypeDB.Transaction that can fail has both a
{:ok, _} | {:error, %TypeDB.Error{}} form and a ! form that raises.
TypeDB.Error carries TypeDB's own error :code, which is what you want to
branch on.
The one exception is transaction/5: it returns whatever your block returned,
so its {:error, _} may be your own value rather than an exception, and a
bang form would have to guess whether to raise it.
How long a call can take
Four options interact, and only the last of them bounds the call rather than one attempt. With the defaults:
| default | what it bounds | |
|---|---|---|
:connect_timeout | 10_000 | opening the socket |
:timeout | 60_000 | waiting for one response |
:max_retries | 1 | how many extra attempts |
:retry_max_delay | 5_000 | one wait between attempts |
:deadline | :infinity | the whole call |
So a retryable call costs at worst
(max_retries + 1) * (connect_timeout + timeout) + max_retries * retry_max_delaywhich is a little over two minutes by default, plus one more
connect_timeout + timeout if the call has to mint a token first. Raising
:max_retries multiplies the first term — that is the arithmetic :deadline
exists for, since setting it makes the whole expression irrelevant: no call
outlives its budget, each attempt is given only what the budget has left, and
a retry that could not finish inside it is not started.
Retries and their backoffs happen in the calling process. Nothing is queued behind a connection, but nothing is running in the caller either.
Logging
The driver logs sparingly, and nothing at all on the happy path — use
TypeDB.Telemetry for that. This is everything it can say:
| level | when |
|---|---|
debug | a request is about to be retried |
debug | a connection process received a message it does not understand |
warning | retries were exhausted and the call is giving up |
warning | a token renewal failed |
error | a connection's transport died and the connection is stopping |
TypeDB.HTTP.Httpc additionally warns once at start-up when it can find no
OS trust store, carrying :typedb_adapter rather than :typedb_connection:
it happens while the adapter is being built, before there is a connection to
name or a :log_level to consult.
Set :log_level on the connection to raise the floor, or :none to silence
it — see TypeDB.Config. It applies per connection, so one noisy connection
can be quietened without filtering the whole application's Logger by module.
Every line carries :typedb_connection in its Logger metadata, and the
retry and give-up lines additionally carry :typedb_method, :typedb_path,
:typedb_attempt and :typedb_error_kind. Configure your backend to keep
them:
config :logger, :default_formatter,
metadata: [:typedb_connection, :typedb_error_kind]Credentials never appear in a log line or in metadata; see TypeDB.Config
for how the connection keeps them out of crash reports too.
Concurrency
Requests run in the calling process, so N processes issue N concurrent
requests; the connection process is consulted only to mint or renew the auth
token. Sockets are pooled by the HTTP adapter — :max_sessions on
TypeDB.HTTP.Httpc caps how many are opened per host.
What this driver covers
Everything in the TypeDB HTTP API v1: sign-in and token renewal, databases, users, servers, version and health, explicit transactions, one-shot queries, and query analysis. TypeDB's gRPC-only features — database import/export and streaming answers — are not available over HTTP and so are not here.
Summary
Types
A connection: the registered name of a TypeDB.Connection process.
Functions
Creates a database. See TypeDB.Database.create/2.
Creates a database, raising on failure. See TypeDB.Database.create!/2.
Lists databases. See TypeDB.Database.list/1.
Lists databases, raising on failure. See TypeDB.Database.list!/1.
Deletes a database and all of its data. See TypeDB.Database.delete/2.
Deletes a database, raising on failure. See TypeDB.Database.delete!/2.
Returns :ok when the server is reachable. See TypeDB.Server.health/1.
Returns :ok when the server is reachable, raising otherwise.
See TypeDB.Server.health!/1.
Runs a single query in a transaction of its own.
Runs a single query, raising TypeDB.Error on failure.
Starts a connection. See TypeDB.Config for options.
Stops a connection, by registered name or pid.
Runs fun inside a transaction, committing on success.
Returns the server distribution and version. See TypeDB.Server.version/1.
Returns the server distribution and version, raising on failure.
See TypeDB.Server.version!/1.
Types
@type conn() :: TypeDB.Connection.t()
A connection: the registered name of a TypeDB.Connection process.
Functions
@spec create_database(conn(), String.t()) :: :ok | {:error, TypeDB.Error.t()}
Creates a database. See TypeDB.Database.create/2.
Creates a database, raising on failure. See TypeDB.Database.create!/2.
@spec databases(conn()) :: {:ok, [String.t()]} | {:error, TypeDB.Error.t()}
Lists databases. See TypeDB.Database.list/1.
Lists databases, raising on failure. See TypeDB.Database.list!/1.
@spec delete_database(conn(), String.t()) :: :ok | {:error, TypeDB.Error.t()}
Deletes a database and all of its data. See TypeDB.Database.delete/2.
Deletes a database, raising on failure. See TypeDB.Database.delete!/2.
@spec health(conn()) :: :ok | {:error, TypeDB.Error.t()}
Returns :ok when the server is reachable. See TypeDB.Server.health/1.
@spec health!(conn()) :: :ok
Returns :ok when the server is reachable, raising otherwise.
See TypeDB.Server.health!/1.
@spec query(conn(), String.t(), String.t(), keyword()) :: {:ok, TypeDB.Answer.t()} | {:error, TypeDB.Error.t()}
Runs a single query in a transaction of its own.
TypeDB opens the transaction, runs the query, then commits or closes it — one HTTP round trip in total, which makes this the cheapest way to run a standalone query.
Options
:transaction_type—:read,:writeor:schema. Defaults to:schema, the only type that accepts every kind of query.A
:schematransaction takes TypeDB's exclusive, database-wide schema lock for the duration of the call. One-shot queries left on the default therefore serialise against each other, and against anything else holding that lock. Passtransaction_type: :readfor reads and:writefor data changes — both are concurrent — and leave the default todefineandundefine. Narrowing also has the server reject an accidental write.:commit— whether to commit a write or schema query. Defaults totrue. Read queries never commit.:given_rows— input rows for the query'sgivenstage; seeTypeDB.Transaction.query/3for how to parameterise a query safely.plus all query and transaction options from
TypeDB.Options, and:timeoutand:deadline— the first bounds one attempt, the second the whole call including retries. SeeTypeDB.Config.
Raises
Unlike the rest of this module, query/4 has two failure paths. Anything the
server rejects comes back as {:error, %TypeDB.Error{}}; anything rejected
before the request is built raises, because there is no request to fail:
ArgumentErrorfor an invalid:transaction_type— that is a literal in your source, not data.TypeDB.Errorwith kind:encodefor a:given_rowsvalue the driver cannot turn into a TypeDB wire value, including a negativeTypeDB.Duration.
Examples
TypeDB.query(conn, "social", "match $p isa person; select $p;",
transaction_type: :read,
answer_count_limit: 100
)
# Dry run: execute the write, then throw it away.
TypeDB.query(conn, "social", "insert $p isa person;", commit: false)
# Parameterised, and therefore safe against TypeQL injection.
TypeDB.query(conn, "social", """
given $n: string;
insert $p isa person, has name == $n;
""", given_rows: [%{"n" => user_supplied_name}])
@spec query!(conn(), String.t(), String.t(), keyword()) :: TypeDB.Answer.t()
Runs a single query, raising TypeDB.Error on failure.
@spec start_link(keyword()) :: GenServer.on_start()
Starts a connection. See TypeDB.Config for options.
Stops a connection, by registered name or pid.
@spec transaction( conn(), String.t(), TypeDB.Transaction.type(), (TypeDB.Transaction.t() -> result), keyword() ) :: result | {:error, TypeDB.Error.t()} when result: term()
Runs fun inside a transaction, committing on success.
The transaction is committed when fun returns, rolled back when it returns
{:error, _}, and rolled back before the exception propagates when it raises,
throws or exits. :read transactions are closed rather than committed, since
there is nothing to commit.
Returns whatever fun returned, except that a successful :write/:schema
block whose commit fails returns {:error, %TypeDB.Error{}}.
Options
Transaction options from TypeDB.Options, plus :timeout and :deadline,
which are forwarded to the request that opens the transaction.
Examples
TypeDB.transaction(conn, "social", :write, fn tx ->
TypeDB.Transaction.query!(tx, "insert $p isa person, has name 'Alice';")
TypeDB.Transaction.query!(tx, "insert $p isa person, has name 'Bob';")
:ok
end)
#=> :ok
# Returning {:error, _} rolls back and returns that error unchanged.
TypeDB.transaction(conn, "social", :write, fn tx ->
case TypeDB.Transaction.query(tx, "insert $p isa person;") do
{:ok, _} -> {:error, :changed_my_mind}
error -> error
end
end)
#=> {:error, :changed_my_mind}
@spec version(conn()) :: {:ok, TypeDB.Server.version()} | {:error, TypeDB.Error.t()}
Returns the server distribution and version. See TypeDB.Server.version/1.
@spec version!(conn()) :: TypeDB.Server.version()
Returns the server distribution and version, raising on failure.
See TypeDB.Server.version!/1.