• Start

Using SurrealKit as a library

Library usage example

Worked examples of using SurrealKit's library API for both sync and rollouts in a Rust application.

Worked examples of using SurrealKit's library API directly in Rust, covering both sync (for development and ephemeral databases) and rollouts (for shared and production databases).

For the macro-based approach to sync, see the embed_schema! macro.

Both examples below assume the following Cargo.toml and connection setup.

[package]
name = "my-app"
version = "0.1.0"
edition = "2021"

[dependencies]
surrealkit = "0.7"
surrealdb = "2"
tokio = { version = "1", features = ["full"] }
anyhow = "1"
use surrealkit::{DbCfg, DbOverrides, connect};

let cfg = DbCfg::from_env(None, &DbOverrides::default())?;
let db = connect(&cfg).await?;

DbCfg::from_env reads SURREALDB_HOST, SURREALDB_NAMESPACE, SURREALDB_NAME, SURREALDB_USER, and SURREALDB_PASSWORD from the environment or a .env file.

Use sync against local or ephemeral databases where fast iteration matters and pruning is safe. Schema files are embedded at compile time with include_str!, then applied with the Sync builder.

use anyhow::Result;
use surrealkit::{DbCfg, DbOverrides, EmbeddedSchemaFile, Sync, connect, seed, TemplateVars};

static SCHEMA: &[EmbeddedSchemaFile] = &[
EmbeddedSchemaFile {
path: "database/schema/users.surql",
sql: include_str!("../database/schema/users.surql"),
},
EmbeddedSchemaFile {
path: "database/schema/posts.surql",
sql: include_str!("../database/schema/posts.surql"),
},
];

#[tokio::main]
async fn main() -> Result<()> {
let cfg = DbCfg::from_env(None, &DbOverrides::default())?;
let db = connect(&cfg).await?;

// Defaults: prune = true, fail_fast = true.
Sync::embedded(SCHEMA).run(&db).await?;

// Optionally load seed data after syncing.
seed(&db, "database", &TemplateVars::default()).await?;

Ok(())
}

Cargo re-compiles whenever an include_str!-referenced file changes, so the binary always reflects the latest schema. Sync::embedded(...).run(...) applies new or changed definitions and removes any that have been deleted from the schema files.

Seed files run on every startup, so use UPSERT or guard inserts if idempotency matters.

Use rollouts for shared and production databases. Build a RolloutSpec with the builder, wrap it in the Rollout facade, then call start and complete at the appropriate points around your application deployment.

use anyhow::Result;
use surrealkit::{
DbCfg, DbOverrides, connect,
Rollout, RolloutSpec, RolloutStep, RolloutPhase, RolloutCompatibility,
EntityKey, EntityKind,
};

#[tokio::main]
async fn main() -> Result<()> {
let cfg = DbCfg::from_env(None, &DbOverrides::default())?;
let db = connect(&cfg).await?;

let spec = RolloutSpec::builder("20260604__add_account")
.name("Add account table")
.compatibility(RolloutCompatibility::Phased)
.step(RolloutStep::apply_schema(
"create_account", RolloutPhase::Start,
"DEFINE TABLE account SCHEMAFULL;",
))
.step(RolloutStep::remove_entities(
"rollback_account", RolloutPhase::Rollback,
vec![EntityKey { kind: EntityKind::Table, scope: None, name: "account".into() }],
))
.build();

// `&[]` here means the steps fully describe the entity changes. Pass the
// desired post-rollout schema files to have SurrealKit compute the catalog.
let rollout = Rollout::new(spec, &[]);

rollout.start(&db).await?;

Ok(())
}

After start succeeds, deploy your application. Once the new version is stable, call complete to apply the contract phase:

rollout.complete(&db).await?;

If something goes wrong before completing, roll back the start phase:

rollout.rollback(&db).await?;

If a process is killed mid-rollout, the __rollout row can be left in a running_* state. Inspect it with rollout.status(&db).await? and recover it with the CLI rollout repair command or Rollout::abandon(&db, "20260604__add_account").await?.

Was this page helpful?