Database Sync

Radiant syncs your database directly from the compiled schema — no migration files, no version numbers, no up/down scripts. Run radiant db:sync and the runtime diffs your schema against the live database, then applies the changes.

How It Works

┌───────────────┐     ┌──────────────┐     ┌──────────────────┐
│  schema.json  │────▶│  Schema Diff  │────▶│  Database Changes │
│  (compiled)   │     │  (add/create/ │     │  (applied safely) │
│               │     │   drop)       │     │                   │
└───────────────┘     └──────┬───────┘     └──────────────────┘
                      ┌──────────────┐
                      │  Live Database│
                      │  (adapter)    │
                      └──────────────┘
  1. radiant db:sync loads radiant/schema.json (produced by radiant generate)
  2. Connects to the database using DATABASE_URL
  3. Introspects the current database schema (tables, columns)
  4. Computes a diff between the compiled schema and the live database
  5. Applies non-destructive changes automatically
  6. Applies destructive changes only with --force

Supported Databases

The adapter is selected based on the DATABASE_URL scheme:

URL SchemeDatabaseAdapter Package
file:, sqlite:SQLite@codesordinatestudio/radiant-plugin-sqlite
postgres:, postgresql:PostgreSQL@codesordinatestudio/radiant-plugin-postgres
http:, https:SurrealDB@codesordinatestudio/radiant-plugin-surrealdb

Adapter plugins are resolved from your project's node_modules — not from the CLI's bundled dependencies.

The Diff Report

After comparing the schema against the database, db:sync prints a report:

📊 Comparing schema against database...

  + Tables to create:
      + todos
      + products
  + Columns to add:
      + users.role
      + users.avatar
  - Tables to drop:
      - legacy_table
  - Columns to drop:
      - users.legacyField

Change Types

TypeDestructive?DefaultWith --force
Create table — collection doesn't exist in DBNo✅ Applied✅ Applied
Add column — field doesn't exist in tableNo✅ Applied✅ Applied
Drop table — DB table not in schema (orphaned)Yes⏭️ Skipped✅ Applied
Drop column — DB column not in schema (orphaned)Yes⏭️ Skipped✅ Applied

Safe Mode (Default)

Without --force, the sync only applies additive changes:

radiant db:sync
  • ✅ Creates missing tables
  • ✅ Adds missing columns
  • ⏭️ Skips dropping orphaned tables
  • ⏭️ Skips dropping orphaned columns

This is safe to run in production — it never destroys data.

Force Mode

With --force, the sync also applies destructive changes:

radiant db:sync --force
  • ✅ Creates missing tables
  • ✅ Adds missing columns
  • ✅ Drops orphaned tables
  • ✅ Drops orphaned columns

In production (NODE_ENV=production), destructive changes are always skipped unless --force is explicitly provided, even if --force would normally apply them.

System Tables

The sync preserves certain system tables that are not defined in the schema:

TablePurpose
radiant_migrationsInternal migration tracking
radiant_refresh_tokensJWT refresh token storage
radiant_audit_logAudit log entries (auto-created when security.audit.enabled is true)

These tables are never dropped, even if they appear orphaned.

Auto-Injected Collections

When security.audit.enabled is true, the compiler automatically injects a radiant_audit_log collection into the schema with these fields:

FieldTypeOptional
actiontextNo
collectiontextYes
recordIdtextYes
userIdtextYes
metadatajsonYes
hmactextNo
prevHmactextYes

The HMAC fields create a tamper-evident chain — each log entry signs the previous entry's HMAC, so any modification is detectable.

Workflow

# 1. Edit your .radiant files
# Add a new field to a collection:
#   collection products {
#     fields: {
#       + sku: text @unique
#     }
#   }

# 2. Recompile
radiant generate

# 3. Sync the database
radiant db:sync
# Output:
#   + Columns to add:
#       + products.sku
#   ⚙️  Applying changes...
#   ✅ Schema sync complete.

# 4. If you removed a field and want to drop the column:
radiant db:sync --force

Environment Variables

db:sync automatically loads .env from the project root if it exists:

# .env
DATABASE_URL=file:./radiant.sqlite
# or
DATABASE_URL=postgres://user:pass@localhost:5432/mydb

For SurrealDB, additional environment variables are used:

SURREAL_USER=root
SURREAL_PASS=root
SURREAL_NS=test
SURREAL_DB=test