Skip to content

SurrealKit

Seeding

Seeding applies .surql data files to a database: reference data, default records, a demo dataset. It is separate from schema sync, which manages definitions rather than rows.

surrealkit seed --user root --pass secret

Seed files live in database/seed/, which surrealkit init scaffolds. Only files ending in .surql are read, and only at the top level of the directory - subdirectories are ignored. Files run in lexicographic order by filename, so a numeric prefix fixes the sequence when one file depends on another:

database/seed/
├── 000_regions.surql
├── 010_plans.surql
└── 020_demo_users.surql

Available since: v1.0

surrealkit seed is idempotent. SurrealKit records every file it applies in a __seed table and skips the ones that have not changed since, so the command is safe to run on every deploy:

surrealkit seed --user root --pass secret
Output
Seeding from database/seed (3 files found)
  skipping database/seed/000_regions.surql (unchanged)
  skipping database/seed/010_plans.surql (unchanged)
  executing database/seed/020_demo_users.surql
Seeded 1 file(s); 2 unchanged
Important

Before 1.0, surrealkit seed re-ran every file on every invocation. If you used it to reset a development database, that no longer happens - use --force below.

A file is tracked by its path, and compared by a hash of its contents. Editing a file re-runs it; renaming or moving one makes SurrealKit see a removal and a new file, so the new path runs again.

The hash is taken from the file on disk, before template variables are substituted. Changing a --var value therefore does not re-run a seed on its own. Edit the file, or use --force.

--force ignores the tracking table and applies every file:

surrealkit seed --force --user root --pass secret

Seed files that are not written to be idempotent will duplicate their data when re-run. Prefer UPSERT over CREATE, or CREATE with an explicit record id, so that a forced run converges rather than accumulates.

SurrealKit creates __seed in the target database:

DEFINE TABLE IF NOT EXISTS __seed SCHEMAFULL PERMISSIONS NONE;
DEFINE FIELD IF NOT EXISTS key ON __seed TYPE string;
DEFINE FIELD IF NOT EXISTS hash ON __seed TYPE string;
DEFINE FIELD IF NOT EXISTS applied_at ON __seed TYPE datetime DEFAULT time::now();
DEFINE INDEX IF NOT EXISTS by_seed_key ON __seed FIELDS key UNIQUE;

The table is created on the first write of a run rather than upfront, so a run where every file is unchanged performs no schema changes and needs no DEFINE privileges. Reads tolerate its absence and treat every file as new.

Like the other SurrealKit metadata tables, __seed is managed by the tool and should not be edited directly, except when upgrading from an older layout.

Seed files support the same ${VAR} substitution as schema files, so environment-specific values can be parameterised:

-- database/seed/000_regions.surql
CREATE region:eu SET name = 'Europe', bucket = '${asset_bucket}';

An application that ships without its database/ directory can bake the seed files into the binary at compile time with the embed_seed! macro, the counterpart to embed_schema!:

// Reads database/seed/**/*.surql relative to Cargo.toml at compile time.
surrealkit::embed_seed!();

async fn run(db: &surrealkit::Surreal<surrealkit::engine::any::Any>) -> anyhow::Result<()> {
    embedded_seed::seed(db).await?;
    Ok(())
}

Embedded seeds are tracked in __seed exactly as filesystem seeds are, so an application can call this on every start.

Note

surrealkit seed reads database/seed regardless of --schema, so a project using schema modules cannot yet seed a named module's directory from the CLI in 1.0.0-beta.1. Use the library's Seed builder, or point --folder at the module.

Was this page helpful?