# 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](/docs/manage/schema-migration/embed-schema-macro.md).

## Shared setup

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

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

[dependencies]
surrealkit = "0.7"
surrealdb = "2"
tokio = { version = "1", features = ["full"] }
anyhow = "1"
```

```rust
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.

## Sync

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`](/docs/manage/schema-migration/library.md) builder.

```rust
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.

## Rollouts

Use rollouts for shared and production databases. Build a [`RolloutSpec`](/docs/manage/schema-migration/library.md) with the builder, wrap it in the `Rollout` facade, then call `start` and `complete` at the appropriate points around your application deployment.

```rust
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:

```rust
rollout.complete(&db).await?;
```

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

```rust
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`](/docs/manage/schema-migration/rollouts.md) command or `Rollout::abandon(&db, "20260604__add_account").await?`.
</content>
