surrealkit: Rust library
This document covers SurrealKit as a Rust library. If you are looking for the CLI, see the project README.
The library is useful when you want schema management to happen inside your process at startup — for example with an embedded SurrealDB backend (RocksDB, SpeeDB) or when running SurrealDB in the same binary during tests.
Add to your project
[]
= "0.7"
Concepts: sync vs rollout
SurrealKit gives you two ways to get schema into a database. Pick based on whether the database is disposable or shared.
| Sync | Rollout | |
|---|---|---|
| Mental model | Declarative desired state — "make the DB match this schema" | Staged, reviewable migration with an explicit undo |
| Applies | All changed files, idempotently | Ordered steps across start / complete / rollback phases |
| Removes objects | Automatically (prune) | Only in the complete phase, via explicit steps |
| Reversible | No | Yes (rollback) |
| Use when | Dev/test/CI, single-owner or embedded databases | Shared/production databases where you need expand→contract and a rollback path |
The two compose: use sync for everyday schema and reach for a rollout when a change needs to land safely while old and new code run side-by-side.
Connecting
DbCfg reads connection details from environment variables (with optional overrides); connect builds the surrealdb::Surreal client and authenticates:
use ;
# async
For an in-process SurrealDB (e.g. mem://, rocksdb://), construct a Surreal directly and pass it to any library function:
use connect;
use Config;
use Capabilities;
# async
Schema sync
embed_schema! (compile-time embedding)
embed_schema! walks your .surql files at build time and bakes them into the binary. The generated embedded_schema::sync applies any file whose content changed:
// Reads database/schema/**/*.surql relative to your Cargo.toml at compile time.
embed_schema!;
async
A custom path relative to your Cargo.toml may be passed: embed_schema!("my/schema/dir"). The generated module is always named embedded_schema.
Sync builder (runtime control)
To build the schema slice yourself, or to customize behavior, use the [Sync] builder:
use ;
use Any;
static SCHEMA: & = &;
# async
Sync calls setup internally and reads nothing from the filesystem — it never writes scaffolding files.
EmbeddedSchemaFile: path vs sql
This trips people up, so to be explicit:
pathis a stable tracking key, not a path that must exist on disk. SurrealKit stores it in its metadata tables to identify the file, detect content changes, and prune files that disappear. Keep it stable across releases — renaming it makes SurrealKit treat the old key as deleted and the new one as added.sqlis the content that gets applied. Changingsqlwhile holdingpathconstant is exactly what triggers a re-apply on the next sync.
Rollouts
Rollouts are defined entirely in code — no TOML or .surql files on disk required. Build a spec with [RolloutSpec::builder] and drive it with the [Rollout] facade.
Status lifecycle
planned → running_start → ready_to_complete → running_complete → completed
│
└── running_rollback → rolled_back
completed and rolled_back are terminal. failed and the running_* states are stuck states from an interrupted run — recover them with [Rollout::abandon] (or the CLI repair command). Only one rollout may be in a non-terminal state at a time.
Lifecycle example
use ;
use Any;
// The desired schema once the rollout completes (used to compute the managed
// catalog). Pass `&[]` if your steps fully describe the entity changes.
static TARGET: & = &;
# async
Step actions
Each [RolloutStep] carries exactly one action, built with a constructor — invalid combinations cannot be represented:
| Constructor | What it does |
|---|---|
RolloutStep::apply_schema(id, phase, sql) |
Apply inline DDL (OVERWRITE is injected; safe to retry) |
RolloutStep::run_sql(id, phase, sql) |
Run data-mutation SQL (must be safe to re-run) |
RolloutStep::assert_sql(id, phase, sql, expect) |
Assert a query's output equals expect |
RolloutStep::remove_entities(id, phase, entities) |
REMOVE … IF EXISTS the given objects |
Recovery / stuck rollouts
If a process dies mid-rollout, the rollout is left in a running_* or failed state and blocks new rollouts. To inspect and recover:
# use ;
# use Any;
# async
Seeding
[seed] runs .surql files from a seed/ directory (lexicographic order), applying template variables:
# use ;
# use Any;
# async
Template variables
${VAR} placeholders in schema/seed/rollout SQL are substituted from a [TemplateVars] map before execution (lookups are case-insensitive; undefined variables are an error that names the missing key and file). Pass them via Sync::vars(...), Rollout::vars(...), or seed.
Metadata tables
SurrealKit maintains two internal tables in your namespace/database, created automatically:
| Table | Purpose |
|---|---|
__entity |
Tracks every schema object SurrealKit manages (content hash, tracking key) |
__rollout |
Tracks rollout execution state (see the status lifecycle above) |