falkordb-rs
The official Rust client for FalkorDB — a fast, low-latency
graph database. One ergonomic API across a blocking client and an async (tokio) client, with
typed results, parameter binding, batching, an embedded server, TLS, automatic retries, and
OpenTelemetry-aligned tracing and metrics.
Highlights
- Sync and async — a blocking client and a
tokioasync client share the same ergonomic API. - Header-aware, typed results — read columns by name or index, or map rows straight into your own
serdetypes. - Safe parameters — bind Rust values as Cypher literals; no hand-quoting, no injection.
- Batching and pipelining — send many queries in a single round-trip.
- Async streaming — result sets are
Streams that compose with the fullfuturestoolbox. - Resilient — opt-in
RetryPolicywith bounded backoff for transient failures; writes are never retried. - Observable — OpenTelemetry-aligned
tracingspans andmetricscounters/histograms, privacy-safe by default. - Replica-aware — opt in to routing read-only queries to replicas behind Redis Sentinel.
- Embedded server — spin up a self-contained FalkorDB for tests and prototyping, with an optional build-time bundle mode that runs fully offline.
Table of contents
- Highlights
- Quickstart
- Cargo feature flags
- Guide
- Examples
- API documentation
- Migration guides
- Contributing
- Community and license
Quickstart
Install
Install it with cargo add:
Run a FalkorDB server
Docker:
Your first query
use ;
// Connect to FalkorDB
let connection_info: FalkorConnectionInfo = "falkor://127.0.0.1:6379".try_into
.expect;
let client = new
.with_connection_info
.build
.expect;
// Select the social graph
let mut graph = client.select_graph;
// Create 100 nodes and return a handful
let mut nodes = graph.query
.with_timeout
.execute
.expect;
// Each item is a `FalkorResult<Row>`; read columns by index or name.
while let Some = nodes.data.next
Cargo feature flags
All features are off by default — enable only what you need:
| Feature | Enables |
|---|---|
tokio |
The async client and API on the tokio runtime (multi-threaded scheduler). |
serde |
Map query results into your own serde::Deserialize types. |
tracing |
OpenTelemetry-aligned tracing spans with a privacy-safe query fingerprint. |
metrics |
Counters and histograms via the metrics facade (install any exporter). |
embedded |
Run a self-contained embedded FalkorDB server (module downloaded at runtime). |
embedded-bundle |
Embed the module at build time so the embedded server runs fully offline. |
rustls / native-tls |
TLS for the sync client, via rustls or native-tls. |
tokio-rustls / tokio-native-tls |
TLS for the async client. |
Guide
Each capability below has a short explanation, a minimal snippet, and a link to a complete,
runnable example. The async client mirrors the sync API — await the terminals.
Queries and results
Header-aware result rows
QueryResult::data iterates the result set as FalkorResult<Row>. Each Row pairs the query
header (the column aliases) with that row's values, so you read columns by name or index and a
row that fails to parse surfaces as an Err instead of being silently swallowed:
use ;
let connection_info: FalkorConnectionInfo = "falkor://127.0.0.1:6379".try_into
.expect;
let client = new
.with_connection_info
.build
.expect;
let mut graph = client.select_graph;
let mut result = graph
.query
.execute
.expect;
for row in result.data.by_ref
Row offers borrowing accessors (get, get_at, get_all), typed accessors
(try_get::<T>, try_get_at::<T>), and consuming conversions (into_values, into_map). Typed
access is strict — no silent lossy casts — via the FromFalkorValue conversion trait. Because
collect short-circuits on the first Err, a whole result set can be gathered with
result.data.collect::<falkordb::FalkorResult<Vec<_>>>().
FalkorDB rejects a query whose result columns are not uniquely named, so rows from a query always
have distinct columns; if a Row ever does hold duplicates, the access paths are still defined
(get/try_get return the first match, get_all returns every match, into_map keeps the last).
To opt back into the pre-0.7 behavior (bare Vec<FalkorValue> rows, parse errors collapsed to
FalkorValue::Unparseable), call result.data.into_values_lossy(). A runnable version lives in
examples/rows.rs. Upgrading from 0.6? See the
0.7 migration guide.
Type-safe query parameters
Pass Rust values straight into a query — the client encodes them as Cypher literals and escapes them for you, so you never hand-quote strings or risk Cypher injection:
let res = graph
.query
.with_param
.with_param
.execute?;
Add several at once from an array, Vec, or map with with_params (the values share a single
type; use chained with_param calls, as above, for a mix of types):
.with_params
Supported value types include integers, floats, boolean values, strings, Option (encoded as
null), arrays/Vec, and string-keyed HashMap/BTreeMap (nested freely). Points and vectors
cannot be bound directly (a FalkorDB limitation) — pass the components and construct them in the
query:
use BTreeMap;
let coords = from;
graph.query.with_param.execute?;
If you really need a raw Cypher expression, with_raw_param("key", "…") is the explicit escape
hatch — no escaping is applied to the value (the parameter name is still validated).
Temporal values returned by queries — datetime, date, time/localtime and duration —
decode into the typed DateTime, Date, Time and Duration values. Each exposes its scalar as a
typed Seconds (value.seconds()), and DateTime/Duration support a small type-safe algebra
(DateTime - DateTime → Duration, DateTime ± Duration → DateTime, plus Duration
add/subtract/negate) with overflow-checked checked_* variants. They are read from results but
cannot be bound back as parameters — build them in the query with the matching Cypher function (e.g.
date($s)). A runnable version lives in examples/temporal.rs.
Typed result mapping with serde
Enable the optional serde feature to map query results straight into your own types instead of hand-matching every
FalkorValue variant:
Derive serde::Deserialize on your type and call FalkorValue::deserialize_into (or the free function
falkordb::from_falkor_value) on a returned value. A node is deserialized from its properties, and scalars, Option,
sequences and maps map onto the matching Rust types:
use ;
use Deserialize;
let connection_info: FalkorConnectionInfo = "falkor://127.0.0.1:6379".try_into
.expect;
let client = new
.with_connection_info
.build
.expect;
let mut graph = client.select_graph;
let mut result = graph.query.execute
.expect;
for row in result.data.by_ref
A runnable version lives in examples/typed_mapping.rs.
To map a whole result set in one shot, call query_as::<T>() before execute(). Each row is
deserialized into a T, and the result's data becomes an iterator of FalkorResult<T>, so it
collects directly into a Vec:
let movies: = graph
.query
.
.execute
.expect
.data
.
.expect;
A single-column row (such as RETURN m) is deserialized from that one column's value, so a node
maps from its properties and RETURN count(m) maps a scalar. A multi-column row (such as
RETURN m.title AS title, m.year AS year) maps each column alias onto the matching struct field,
or yields the values in order for a tuple. The query header and stats remain available on the
returned result.
Async
tokio support
This client supports nonblocking API using the tokio runtime.
It can be enabled like so:
Currently, this API requires running within a
multi_threaded tokio scheduler, and
does not support the current_thread one, but this will probably be supported in the future.
The API uses an almost identical API, but the various functions need to be awaited:
use ;
use StreamExt; // brings `.next().await` onto the result stream
// Connect to FalkorDB
let connection_info: FalkorConnectionInfo = "falkor://127.0.0.1:6379".try_into
.expect;
let client = new_async
.with_connection_info
.build
.await
.expect;
// Select the social graph
let mut graph = client.select_graph;
// Create 100 nodes and return a handful
let mut nodes = graph.query
.with_timeout
.execute
.await
.expect;
// `nodes.data` is a `Stream<Item = FalkorResult<Row>>`; pull rows with `.next().await`:
while let Some = nodes.data.next.await
The result set (nodes.data) is an owned, Send + 'static Stream, so it can be moved into a
spawned task and driven with the full StreamExt / TryStreamExt toolbox. The graph
handle itself is Send + Clone: cloning is cheap and shares one schema cache, so to use a graph
from several concurrent tasks you just clone it — no Arc<Mutex<_>> wrapping required.
Async streaming
Because results are a Stream, the standard combinators just work. Import the extension traits
(use futures::{StreamExt, TryStreamExt};) and:
use ;
// Collect a typed stream in one line (errors short-circuit):
let years: = graph
.query
.execute
.await?
.data
.map
.try_collect
.await?;
// Move a result stream into its own task (it is `Send + 'static`):
let mut stream = graph.query.execute.await?.data;
let count = spawn
.await
.unwrap?;
// Fan out a follow-up query per row with bounded concurrency, over cloned handles:
let enriched: = graph
.query
.execute
.await?
.data
.map
.buffer_unordered
.try_collect
.await?;
A runnable version lives in examples/async_stream.rs.
Connection strategy and multiplexing
The asynchronous client chooses how it manages its underlying Redis connections via a
ConnectionStrategy:
Multiplexed(the async default): a small number of shared, cloneable, auto-reconnecting connections. Many concurrent commands are pipelined over each socket, so a single connection can carry many in-flight requests at once. This avoids the borrow/return bottleneck and is the most efficient option for highly concurrent workloads.Pooled: a fixed pool of independent connections, each used by exactly one command at a time (borrow/return). This gives strict per-command isolation and a natural cap on in-flight commands. It is the only strategy for the synchronous client.
Select or tune the strategy on the builder:
use ;
use NonZeroU8;
// Spread commands across 4 shared multiplexed sockets (the default uses 8).
let client = new_async
.with_connection_strategy
// Optional backpressure: cap concurrently in-flight commands per socket.
.with_max_inflight
.build
.await
.expect;
assert_eq!;
Notes and caveats:
- Behavior change: the async default is now multiplexed (previously an exclusive
borrow-pool). The API is source-compatible;
with_num_connectionsnow sets the number of underlying connections/sockets for the active strategy, andconnection_pool_size()reports that count. - Backpressure: multiplexed mode does not bound the number of outstanding requests
unless you set
with_max_inflight(n)(wherenis aNonZeroUsize; ignored by the pooled strategy, whose pool size already caps in-flight commands). - Sentinel: a multiplexed connection built from a Sentinel-resolved node would not
re-resolve the master/replica on failover, so for Sentinel deployments the client
transparently falls back to the pooled strategy (which re-resolves on reconnect).
connection_strategy()returns this effective strategy.
A runnable example is provided in examples/multiplexed_async.rs.
Execution patterns
Batch and pipelined execution
Normally each query is one network round-trip. graph.batch() queues several queries and sends them
over a single Redis pipeline in one round-trip, returning one result per query in submission
order. Queue queries with query (a GRAPH.QUERY) / ro_query (a GRAPH.RO_QUERY) and set
per-query parameters on the returned handle:
let mut batch = graph.batch;
for movie in &movies
batch.ro_query;
let results = batch.execute?; // Vec<BatchItemResult>, one per query, in order
for in results.into_iter.enumerate
On the async client it is identical but for the await:
let mut batch = graph.batch;
// … queue queries …
let results = batch.execute.await?;
Key points:
- Per-item errors. A failing query (bad Cypher, or a parameter that can't be encoded) becomes
that slot's
Err; the other queries are unaffected. The outerResultonly fails if the whole batch could not be completed — and if that happens after the pipeline was sent, the server may have run some or all queries (the state is unknown), which matters for writes. - Not a transaction. A pipeline is not
MULTI/EXEC: every queued query is executed, so a failure in one does not roll back or stop the others. - Results are eager. Each query's rows are parsed up front into a
Vec<Row>(the sameRowas elsewhere), since many result sets coexist in one batch. - Owned queries. To build queries ahead of time, construct
BatchQuery::write(..)/BatchQuery::read(..), attach params/timeout, andbatch.push(query).
A runnable version lives in examples/batch.rs.
Waiting for background operations
Some FalkorDB operations finish after the command that starts them returns: when you create or
drop an index or constraint, the request returns immediately while the index is populated (or the
constraint is enforced) on a background worker thread, and GRAPH.COPY can fail transiently while
the server is unable to fork. The eager methods
(create_index, create_unique_constraint, copy_graph, …) stay fire-and-forget, but every
one of them now has an additive *_op builder that adds explicit, opt-in waiting while keeping
full backward compatibility.
Each builder offers .execute() (non-blocking, identical to the eager method) and .wait() /
.wait_with(WaitOptions) terminals. For index and constraint builders, .wait() blocks until the
operation has actually taken effect (the index/constraint becomes operational or is dropped),
returning FalkorDBError::Timeout if it does not happen in time. For the copy builder, GRAPH.COPY
is already blocking on the server, so .wait() simply retries transient could not fork failures
with backoff; it does not verify the copied contents (that remains the caller's responsibility).
use ;
use Duration;
let connection_info: FalkorConnectionInfo = "falkor://127.0.0.1:6379".try_into
.expect;
let client = new
.with_connection_info
.build
.expect;
let mut graph = client.select_graph;
// Fire-and-forget, exactly like `create_index` (returns as soon as the server accepts it):
graph.create_index_op
.execute
.expect;
// Block until the index is actually operational (default 30s readiness timeout):
graph.create_index_op
.wait
.expect;
// A unique constraint reports a *distinct* error if existing data violates it:
match graph.create_unique_constraint_op
.wait_with
// Copy a graph, retrying transient `could not fork` failures:
let _copy = client.copy_graph_op
.wait
.expect;
The same builders exist on the async client — just await the terminals. See
examples/waiting_ops.rs for a complete, runnable example.
For vector indexes, the typed helpers create_node_vector_index / create_edge_vector_index take a
dimension and a VectorSimilarity (Euclidean or Cosine) and generate the correct
OPTIONS { dimension: N, similarityFunction: '…' } clause for you. Like the other index operations
they are fire-and-forget, and they have matching create_node_vector_index_op /
create_edge_vector_index_op builders that integrate with the waiting ergonomics above —
.wait() blocks until the vector index is operational (and .execute() is the non-blocking
equivalent). A runnable version lives in
examples/vector_index.rs.
Connections and networking
TLS support
This client is currently built upon the redis crate, and therefore supports TLS
using
its implementation, which uses either rustls or
native_tls.
This is not enabled by default, and the user just opt-in by enabling the respective features: "rustls"/"native-tls" (
when using tokio: "tokio-rustls"/"tokio-native-tls").
For Rustls:
For Native TLS:
A runnable example is provided in examples/tls.rs.
TCP keepalive
Long-lived clients behind NATs, stateful firewalls, or idle-timeout-enforcing proxies can silently lose their TCP sessions. The builder exposes TCP-level socket settings to prevent this:
use FalkorClientBuilder;
use Duration;
// Convenience: just enable keepalive with a 30-second idle timeout
let client = new
.with_tcp_keepalive
.build
.expect;
// Or full control via redis::io::tcp::TcpSettings
let settings = default
.set_nodelay
.set_keepalive;
let client = new
.with_tcp_settings
.build
.expect;
Note: TCP settings apply to direct Redis TCP connections only. Unix-domain socket / embedded connections and the Sentinel connection path are not affected.
Read-only queries and replica routing
Read-only queries (ro_query and call_procedure_ro) send GRAPH.RO_QUERY, which the server
refuses to let write. Where such a query runs is a separate, opt-in choice expressed with
ReadPreference. Because a FalkorDB replica applies writes only after the primary, a read
served from a replica can be slightly stale, so the default (ReadPreference::Primary)
keeps every read on the primary — you never observe replication lag unless you ask for it.
Opt into replicas either per client or per query:
- Per client —
with_read_preferencesets the default for every read-only query. - Per query —
prefer_replicaopts a single query in, andprimary_onlyforces one back onto the primary (read-your-writes). The per-query choice overrides the client default.
Replica routing requires a Redis Sentinel deployment that exposes readable replicas; when none is
available (for example a single node), ReadPreference::PreferReplica transparently falls back
to the primary, so the same code runs everywhere. Writes always go to the primary — asking for a
replica on a writable query/call_procedure/batch fails with
FalkorDBError::ReadPreferenceNotReadOnly.
Connection pool sizing: When readable replicas are present the client opens a second pool of up to
num_connectionsadditional connections (one per slot) alongside the primary pool, regardless of the read preference. Size your pool limits and file-descriptor limits accordingly.
use ;
let client = new
// A Sentinel endpoint, e.g. falkor://127.0.0.1:26379
.with_connection_info
// Prefer replicas for this client's read-only queries (accepts slightly stale reads).
.with_read_preference
.build
.expect;
// Capability (a replica pool exists) vs policy (the default routing).
if client.replica_reads_available
println!;
let mut graph = client.select_graph;
// Writes go to the primary.
graph.query.execute.expect;
// Follows the client default (a replica when available, else the primary).
let mut nodes = graph.ro_query.execute.expect;
// Force the freshest data from the primary for a single read, overriding the default.
let mut fresh = graph.ro_query.primary_only.execute.expect;
Against a single node (or any deployment without readable replicas),
replica_reads_available returns false and reads
use the primary. See examples/readonly_replica.rs
for a complete working example.
Resilience and observability
Automatic retries
A client can opt in to a RetryPolicy that automatically re-issues eligible operations on
transient connection failures, with bounded backoff. It is disabled by default, so a client
built without one behaves exactly as before (every operation is attempted once):
use ;
use Duration;
let client = new
.with_retry_policy
.build?;
The same with_retry_policy(..) is available on the async builder
(FalkorClientBuilder::new_async()).
Write safety. The only scope available today, RetryScope::ReadOnly, retries read-only /
idempotent operations only (ro_query, explain, list_indices, list_constraints, read-only
procedure calls). Writes are never retried, so enabling a policy can never duplicate a write.
Classification is by the API you call, never by inspecting Cypher — query() is treated as a write
even when it only reads, so use ro_query() for retryable reads.
Only transient connection errors are retried (a dropped/unavailable connection, or a Sentinel resolution failure); deterministic errors (syntax, constraint violations, parse/type errors, wait-operation timeouts) are returned immediately. Retries compose with the client's existing connection healing: each attempt re-borrows a connection, so a recovered connection is picked up on the next try.
Scope. Retry currently wraps query and procedure execution — ro_query, query, explain,
profile, call_procedure/call_procedure_ro, and list_indices/list_constraints (only the
read-only ones are eligible). Direct client/admin calls (list_graphs, configuration getters/setters,
slowlog, server INFO) and the internal schema-cache refresh that can run while a result is parsed
are not wrapped yet, so a transient failure there still surfaces even with a policy enabled.
Broadening the coverage is a planned follow-up.
See examples/retry.rs for a complete, runnable example.
Tracing
This crate fully supports instrumentation using the tracing crate, to use
it, simply, enable the tracing feature:
Note that different functions use different filtration levels, to avoid spamming your tests, be sure to enable the correct level as you desire it.
When the tracing feature is enabled, the query- and procedure-execution spans are enriched with
structured, low-cardinality fields you can slice and filter on (named after the
OpenTelemetry database conventions so they
map cleanly when exported via tracing-opentelemetry):
| Field | Example | Meaning |
|---|---|---|
db.system.name |
falkordb |
constant |
db.namespace |
social |
the graph name |
db.operation.name |
GRAPH.RO_QUERY / db.idx.fulltext.queryNodes |
the command or procedure |
db.falkordb.read_only |
true |
whether the operation is read-only |
db.falkordb.strategy |
multiplexed |
the active connection strategy |
db.query.fingerprint |
a1b2c3d4e5f60718 |
a privacy-safe hash of the query shape |
error.type |
connection_down |
a bounded error kind, recorded on failure |
db.response.returned_rows |
42 |
rows the server returned (on the outer execute span) |
db.falkordb.server_time_ms |
1.18 |
the server's internal execution time, when reported |
Privacy by default. The raw query text and parameter values are never recorded by default —
only the db.query.fingerprint, which is a hash of the query with all literals (strings, numbers,
true/false/null) redacted, so two queries that differ only in their values share a fingerprint
and no value ever enters a span. If you need the raw Cypher for debugging in a trusted environment,
opt in explicitly:
use FalkorClientBuilder;
let client = new
.with_query_logging // records `db.query.text`; off by default
.build?;
Parameter values supplied via with_param are never recorded even when query logging is enabled
(they live in the query preamble, not the query text).
Note: the async query/procedure futures are deeply nested (retry + instrumentation). If you
tokio::spawnthem with thetracingfeature enabled and hit arecursion limit/overflow evaluating ... Senderror, add#![recursion_limit = "256"]to your crate root — the standard fix for deepasync+tracingstacks.
See examples/observability.rs for a complete, runnable example.
Metrics
Enable the metrics feature to emit counters and histograms through the
metrics facade, so your application can install any
exporter (Prometheus, OpenTelemetry, …):
Each query and procedure execution records:
| Metric | Type | Labels |
|---|---|---|
falkordb_queries_total |
counter | command, operation (read/write), strategy |
falkordb_query_duration_seconds |
histogram | command, operation |
falkordb_query_errors_total |
counter | command, error_kind |
falkordb_retries_total |
counter | operation, error_kind |
falkordb_connections_in_flight |
gauge | route (primary/replica) |
falkordb_connection_pool_wait_seconds |
histogram | route (pooled strategy only) |
All labels are bounded, low-cardinality values: command is an allowlist of known commands
(unknown ⇒ other), operation/strategy/error_kind are small fixed sets. The graph name, query
text, and query fingerprint are never used as labels (they are unbounded and would explode metric
cardinality) — those belong on tracing spans, not metrics. Like tracing, recording is a no-op
until you install a recorder; for example, with metrics-exporter-prometheus:
let builder = new;
builder.install.expect;
// ... use the client; metrics are now exported on the configured endpoint.
Actionable error hints
FalkorDBError::mitigation_hint() turns common, recognizable failures into a short, actionable
remediation tip — handy for logs and AI tooling. It is purely additive: the raw error and its
Display/Debug output are unchanged, hints are fixed &'static strs (so they never echo text from
the underlying message), and unrecognized errors return None.
use FalkorDBError;
let err = ConnectionDown;
if let Some = err.mitigation_hint
Embedded server
This client supports running an embedded FalkorDB server, which is useful for:
- Testing without external dependencies
- Embedded applications
- Quick prototyping and development
To use the embedded feature, enable it:
Choosing a module-provisioning mode
The redis-server binary is never downloaded — it must be installed on the host (see
Requirements). Only the FalkorDB falkordb.so module is provisioned, and there are two
features for that:
-
embedded— runtime download. The module is downloaded on first start (and cached), so the running process needs network access the first time. Best for development. -
embedded-bundle— build-time embed, offline at runtime. Abuild.rsfetches the module for the build target at compile time and embeds it in your binary, so the running process needs no network at all. Best for network-isolated deployments. Enable it instead ofembedded:Control the bundled module at build time with environment variables:
FALKORDB_EMBEDDED_MODULE_VERSION(release tag; defaults to the pinned version),FALKORDB_EMBEDDED_MODULE_PLATFORM(override the asset for distro-specific Linux targets such asrhel9-x64), andFALKORDB_EMBEDDED_MODULE_PATHto embed a local.soinstead of downloading (fully offline builds, or unsupported platforms). A non-default version must be accompanied byFALKORDB_EMBEDDED_MODULE_SHA256— unchecked downloaded native code is never embedded. The downloading build uses the hostcurl(setFALKORDB_EMBEDDED_MODULE_PATHon build hosts withoutcurlor network access); theembedded-bundleruntime itself carries no HTTP/hashing dependencies.License:
embedded-bundleembeds the SSPL-licensed FalkorDB module into your binary, so you are responsible for complying with its license when you distribute that binary.
Requirements
redis-server(version 8.0 or newer) must be installed and available in PATH (or you can specify a custom path). It is not downloaded automatically — install it from your package manager (e.g.brew install redis,apt-get install redis-server).- The
falkordb.somodule is provisioned automatically: downloaded at runtime withembedded(whenauto_downloadis enabled, the default) or embedded at build time withembedded-bundle. You can also pointfalkordb_module_pathat an existing module, or disableauto_downloadto use only explicit/system-installed binaries. - On macOS the module requires OpenMP:
brew install libomp.
Supported platforms: Linux x86_64/aarch64 (glibc and musl/Alpine, plus RHEL 8/9 and Amazon Linux 2023 on x86_64) and macOS aarch64 (Apple Silicon).
Self-contained vs. already-installed
use EmbeddedConfig;
use PathBuf;
// Self-contained (default): download + cache the module if it is missing.
let _auto = default;
// Offline: use only binaries already on the machine (no network access).
let _offline = EmbeddedConfig ;
The cache directory defaults to ~/.cache/falkordb-rs (Linux) or
~/Library/Caches/falkordb-rs (macOS) and can be overridden with the
cache_dir field or the FALKORDB_RS_CACHE_DIR environment variable.
Usage Example
use ;
// Create an embedded configuration with defaults
let embedded_config = default;
// Or customize the configuration:
// let embedded_config = EmbeddedConfig {
// redis_server_path: Some(PathBuf::from("/path/to/redis-server")),
// falkordb_module_path: Some(PathBuf::from("/path/to/falkordb.so")),
// db_dir: Some(PathBuf::from("/tmp/my_falkordb")),
// falkordb_version: None, // pin a different release, e.g. Some("v4.18.10".into())
// cache_dir: None, // override the download cache location
// ..Default::default()
// };
// Build a client with embedded FalkorDB
let client = new
.with_connection_info
.build
.expect;
// Use the client normally
let mut graph = client.select_graph;
graph.query.execute.expect;
// The embedded server will be automatically shut down when the client is dropped
The embedded server:
- Spawns a
redis-serverprocess with the FalkorDB module loaded - Uses Unix socket for communication (no network port)
- Automatically cleans up when the client is dropped
- Can be configured with custom paths, database directory, and socket location
Examples
Every example is a runnable file under examples/ and is compiled in CI.
Run one with cargo run plus the flags shown:
| Example | Shows | Run with |
|---|---|---|
basic_usage |
A minimal connect, query, and iterate flow | --example basic_usage |
rows |
Header-aware rows: read columns by name or index with strict typed access | --example rows |
typed_params |
Type-safe, injection-proof query parameters | --example typed_params |
typed_mapping |
Map query results into your own serde types |
--features serde --example typed_mapping |
temporal |
Decode temporal values and use the type-safe DateTime/Duration algebra |
--example temporal |
vector_index |
Create vector indexes with the typed helpers and VectorSimilarity |
--example vector_index |
batch |
Batch / pipelined execution: many queries in one round-trip | --example batch |
waiting_ops |
Wait for background index / constraint / copy operations to take effect | --example waiting_ops |
udf_usage |
Load a user-defined-function (UDF) library | --example udf_usage |
async_api |
The async (tokio) client end to end |
--features tokio --example async_api |
async_stream |
Async streaming with futures combinators |
--features tokio --example async_stream |
multiplexed_async |
The multiplexed async connection strategy | --features tokio --example multiplexed_async |
readonly_replica |
Route read-only queries to replica nodes | --example readonly_replica |
retry |
The opt-in retry policy for transient failures | --example retry |
tls |
Connect to FalkorDB over TLS | --features rustls --example tls |
observability |
tracing span enrichment and the query fingerprint |
--features tracing --example observability |
embedded_usage |
Run an embedded FalkorDB server | --features embedded --example embedded_usage |
API documentation
The complete API reference is published on docs.rs.
Migration guides
Contributing
Development setup, the full just recipe reference, and how to run the tests and benchmarks live in
CONTRIBUTING.md.
Community and license
Licensed under the MIT License.