Skip to content

SurrealKit

Upgrading to SurrealKit 1.0

Most of a 0.7 project needs no change. Every 0.7 command still works, and running surrealkit sync on an existing project after upgrading re-applies nothing and prunes nothing. This page covers the parts that do need attention, and the new features you can adopt afterwards. Upgrading from 0.7 or from any 1.0 beta carries the existing state in the database forward.

Two of the changes are silent, in the sense that a project keeps running but does something different. Read Rename the DATABASE_* variables and Check whether you pass --folder even if nothing appears broken. If you are on a 1.0 beta, also read Changes since 1.0.0-beta.1.

SurrealKit 1.0.0-rc.1 needs SurrealDB 4.0 or later. It refuses an older server on every command, and in the library's Sync, Rollout, Rollouts and Seed, because it relies on how SurrealDB 4.0 treats records that already exist and writes SurrealQL that only 4.0 reads. Keep SurrealKit 1.0.0-beta.8 until your servers run 4.0.

To upgrade the database, follow Upgrading from 3.x to 4.0. SurrealKit's state in __entity and __rollout comes through unchanged, and a rollout that started on 3.x can finish on 4.0.

SurrealKit 1.0 is a prerelease, so Cargo needs the version:

cargo binstall surrealkit --version 1.0.0-rc.1

surreal install kit installs the same release as the kit component of the SurrealDB CLI.

The single-file seed fallback was deprecated in 0.6, where it printed a warning on every run, and is now removed. Move it into the seed directory:

mkdir -p database/seed
git mv database/seed.surql database/seed/000_init.surql

Seed tracking is keyed by file path, so the moved file counts as a new one and runs again on the next surrealkit seed. If the seed is not idempotent, re-key it first rather than letting it re-run:

-- Setup
CREATE __seed SET key = 'database/seed.surql';

UPDATE __seed SET key = 'database/seed/000_init.surql'
  WHERE key = 'database/seed.surql';

DATABASE_HOST, DATABASE_NAME, DATABASE_NAMESPACE, DATABASE_USER, DATABASE_PASSWORD and DATABASE_AUTH_LEVEL are no longer accepted. Rename each to its SURREALDB_* equivalent.

SurrealKit fails when it finds one set without its replacement, rather than ignoring it:

Error output
the DATABASE_* environment variables were removed in SurrealKit 1.0, but these are still set with no SURREALDB_* replacement:
  DATABASE_HOST -> SURREALDB_HOST
  DATABASE_NAME -> SURREALDB_NAME
Rename them. They are rejected rather than ignored because ignoring them would silently fall back to the defaults and connect to the wrong database.

Failing is deliberate. An ignored DATABASE_HOST would fall back to the default endpoint, so a deployment would connect to the wrong database instead of stopping. Setting both spellings is accepted, and SURREALDB_* wins.

--folder never took effect before 1.0: it was parsed and then discarded, so SurrealKit always used SURREALDB_FOLDER or ./database. It works now.

A project that passed --folder ./db while SurrealKit was really syncing ./database will now sync ./db. If that directory holds no schema files, SurrealKit refuses before opening a connection rather than pruning the database to match:

Error output
refusing filesystem sync: schema_module=default resolved_schema_dir=./db/schema source_count=0; check --folder / SURREALDB_FOLDER and module selection, or pass --allow-empty-prune if the empty source set is intentional

Point --folder at the right directory, drop the flag, or pass --allow-empty-prune when the empty source set is intentional.

Through 0.7, an assertion on a JSON path or a response header that did not exist passed silently. The check compared "not found" against an unset exists field and matched, so the equals, contains or regex comparison was never reached:

[[cases.assertions]]
path = "0.owner"     # a typo, or a query that matched zero rows
equals = "user:alice"

On 0.7 that reported a pass. On 1.0 it fails with path '0.owner' not found. The same applies to header_assertions against a header the response never sent.

Important

If assertions go red on upgrade, they were most likely never being evaluated. Check the actual shape of the result before concluding that SurrealKit regressed.

To assert that something is genuinely absent, say so explicitly:

[[cases.assertions]]
path = "0.secret"
exists = false

exists = false is the only specification that passes on a missing path or header. There is no suite-level opt-out, and unknown keys in an assertion are rejected when the suite is parsed.

surrealkit test now performs a filesystem sync first, so the empty source set check applies to it as well. A suite whose fixtures own the complete schema, with nothing in database/schema, needs --no-sync.

permissions_matrix cases were reworked to handle row-level security, where a filtered result is indistinguishable from a rejected one. Create probes now recreate a record with the same fields, update probes write a marker field first, records created during the matrix are cleaned up, and a permission that skips is reported differently from one that throws. An existing matrix can report a different verdict on 1.0 as a result. Re-read any that change before adjusting them.

  • surrealkit test --schema-from <source> now overrides a suite's own schema_from. Before, a suite that set schema_from ignored the flag.

  • A suite whose preparation fails (setup, sync, seed or fixtures) now has its database removed, like any other suite.

  • A TOML date or time without an offset in signup_params or signin_params, such as dob = 1979-05-27, stays a string.

  • A create rule whose new record a unique index refuses now fails, whatever allow says. Test such creates with a sql_expect case that sets its own unique values.

This is the one silent behaviour change for library users. Rollout::start, Rollout::complete and Rollout::rollback used to default to ./database and create ./database/setup.surql in the caller's working directory. They are now purely in-database:

// 1.0: writes nothing to disk
Rollout::new(spec, files).start(&db).await?;

// 1.0: opt back into the filesystem workflow
Rollout::new(spec, files).folder("database").start(&db).await?;
0.71.0
constants::deprecated_seed_surql_pathRemoved
tester::build_filter_inputFilterInput::from_opts
rollout::run_baseline(db, folder)run_baseline(db, folder, &module)
rollout::run_abandon_rollout(db, id)run_abandon_rollout(db, &module, id)
SyncOpts { .. }Gains module and allow_empty_prune

SurrealKit builds against the SurrealDB 4.0 SDK, so your own surrealdb dependency moves to 4.x with it. The Surreal handle you pass in and the surrealdb_types values that come back are the 4.0 SDK's types, so a project still on the 3.x crate does not compile until it upgrades:

Cargo.toml
[dependencies]
surrealkit = "1.0.0-rc.1"
surrealdb = "4"

The kv-rocksdb feature is gone, because SurrealDB 4.0 has no RocksDB engine; use kv-surrealkv. Other API changes in the 1.0 betas and the release candidate:

Before1.0.0-rc.1
typegen::run_typegen(db, folder, ns, db, opts)run_typegen(db, folder, opts); the namespace and database come from the connection
RolloutActionGains RevertMigration and AwaitMigrations, so a match over it needs arms for them
RolloutAction::ApplyFiles { files: Vec<String> }files: Vec<FileRef>; FileRef::Path(String) is the old form
EntityKindGains Type, for SurrealDB 4.0's DEFINE TYPE
CatalogEntity { .. } and CatalogDiff { .. }Gain a migration and a migrated field
DbOverrides { .. } and DbCfg { .. }Gain connect and query time limit fields
tester::TestOpts { .. }Gains schema_from, exec and exec_root_token, and implements Default
SchemaFile.pathRelative to the project folder (schema/user.surql) rather than to the working directory

Rollouts is new, and runs a project's rollouts in order as rollout up does. A frozen rollout spec runs through Rollout::new(spec, files) only with .folder(..), which says where rollouts/<id>/ is.

If your tests run the embedded 4.0 engine under a current-thread #[tokio::test] runtime and overflow the stack, raise RUST_MIN_STACK to 8 MiB, as SurrealKit's own test configuration does.

Progress output moved to the log facade, so a library consumer sees nothing unless a logger is installed. Set RUST_LOG=surrealkit=info and initialise a logger to get the previous output back. The CLI is unaffected.

clap and its supporting crates are now optional, behind a cli feature that is on by default. A library-only consumer can drop them:

[dependencies]
surrealkit = { version = "1.0.0-rc.1", default-features = false, features = ["kv-mem"] }
Warning

The surrealkit binary declares cli as a required feature. Installing with --no-default-features and without cli builds no binary at all.

These changes arrived in the 1.0 betas after 1.0.0-beta.1 and in the release candidate. A project upgrading straight from 0.7 gets them too.

1.0.0-beta.1 parsed the rollout id on rollout start, complete, rollback, status, lint and repair as a database target name, so each of those commands failed with unknown target "<rollout-id>". Rollout execution was unusable on 1.0.0-beta.1, so upgrade.

--target now selects the database for baseline, start, complete, rollback, repair and up. Before, it was accepted and ignored while the command ran against the ambient SURREALDB_* connection. If you passed --target to those commands, check which database they were really changing. Selecting more than one target is refused.

SurrealKit used to key tracked schema and seed files by their path relative to the working directory, so the same file had a different key in a container whose WORKDIR was not /. Keys are now relative to the project folder, such as schema/001.surql, and are the same in every environment.

You do not need to do anything. The first surrealkit sync after upgrading matches your existing keys by the end of the path and rewrites them in place, and surrealkit seed does the same for __seed. Run sync before seed once on each database, from a checkout where every tracked file still exists. A rollout manifest planned before the upgrade still starts, with a warning; plan again any manifest you have not yet run. If you set your container's WORKDIR to / to work around this, you can remove that.

sync now stops if none of the keys it tracks match a file on disk, because pruning on that would drop the live schema. Pass --allow-empty-prune for a deliberate rewrite in which every schema file path changed at once.

Connecting and signing in now time out after 30 seconds. Change this with --connect-timeout-secs, SURREALDB_CONNECT_TIMEOUT_SECS, or connect_timeout_secs on a target; 0 waits indefinitely. Rollout steps have no time limit unless you set --query-timeout-secs.

rollout plan copies the schema files a rollout changes into database/rollouts/<id>/, and the rollout applies those copies. Commit the directory and ship it with the manifest. A rollout planned before 1.0.0-beta.6 still runs as before, against the schema it was planned from; to let a database catch up across it with rollout up, freeze it. See Rollouts.

A plain DEFINE SEQUENCE is now applied as IF NOT EXISTS, so its counter is not reset. A changed sequence definition therefore does not reach a database where the sequence exists. See How schema files are applied.

-v now prints the version, as -V and --version do. If you passed -v for more detailed logs, pass --verbose. A failure prints error: <message> on one line instead of Error: <message> and a Caused by: list. The exit codes are unchanged.

A manifest's id names its directory, which rollout discard deletes, so an id that contains / or \, or is . or .., is refused. Every id that rollout plan writes is already a plain name, so this affects only an id edited by hand.

typegen now takes its database from --target, the primary target, or the only declared target, before the ambient connection. A project with exactly one declared target therefore introspects that target. Unknown keys in [typegen] are an error, and the formatter runs on a temporary file beside the output. See Type generation.

SurrealDB 4.0 has no RocksDB engine, so the kv-rocksdb cargo feature is gone, and embedded enables kv-surrealkv and kv-mem. A build that names kv-rocksdb fails to resolve; use kv-surrealkv instead.

In a database that SurrealDB 4.0 created, a SCHEMAFULL table that holds records refuses a field change they might not fit, unless the definition has MIGRATE EXISTING ROWS. REMOVE FIELD on such a table also clears the stored values. See Migrating existing records.

  • The package now declares Apache-2.0. It previously declared Unlicense, which did not match the repository.

  • @biomejs/biome moved to devDependencies. It was never imported at runtime, and as a dependency it forced consumers to download the Biome binary. Your lockfile will change.

  • New schemas, targets and all options map to --schema, --target and --all. The default watch globs now cover database/modules/*/schema as well as database/schema.

Nothing below is required, and each is additive.

Schema modules leave your existing schema in the default module with its metadata untouched, and new modules live alongside it:

surrealkit.toml
[schema.billing]
depends_on = ["core"]

Targets name the databases to apply modules to, taking their passwords from the environment:

surrealkit.toml
[target.acme]
ns = "acme"
db = "prod"
pass_env = "ACME_DB_PASSWORD"

Both live in surrealkit.toml, which is now discovered by walking up from the working directory rather than being read from that directory alone.

Was this page helpful?