Programmatic migration API
The CLI is a thin wrapper. Every command — generate, plan, verify, apply, recover — is a function the engine exports. Embed them in your deployment scripts, your internal dashboards, or your own tooling.
Generating and applying
import { diffSchemas, classifyRisk, summarizePlan, applyMigrations, rollbackMigrations, listAppliedMigrations,} from 'zanith'; // Compare two graphs → ops:const ops = diffSchemas(currentGraph, nextGraph); // Score the plan — six levels, twenty reason codes:const summary = summarizePlan(ops);console.log(summary.maxRisk, summary.reasonCounts); // Apply against an adapter:const result = await applyMigrations(adapter, ops, { preflight: true, shadow: true, // …see ApplyOptions}); // List what's already applied:const applied = await listAppliedMigrations(adapter); // Reverse an applied plan:await rollbackMigrations(adapter, [appliedId]);| Export | Purpose |
|---|---|
diffSchemas(from, to) | Structural diff between two SchemaGraph instances → MigrationOp[]. |
reverseOps(ops) | Best-effort reversal — returns ops that undo the input. |
classifyRisk(op) | Risk level + reasons for a single op. |
summarizePlan(ops) | PlanRiskSummary for a whole plan. |
applyMigrations(adapter, ops, opts?) | Run a plan. Returns ApplyResult. |
rollbackMigrations(adapter, ids) | Reverse previously applied plans. |
listAppliedMigrations(adapter) | Read the applied-migrations table. |
Per-step audit — listMigrationSteps
Each op of an applied plan writes a row to _zanith_migration_steps. listMigrationSteps reads that table for a given migration id — useful for building history dashboards or debugging a partial failure.
import { listMigrationSteps } from 'zanith'; const steps = await listMigrationSteps(adapter, '20260504_add_orders_index');for (const step of steps) { console.log(step.opKind, step.startedAt, step.elapsedMs, step.outcome);}MigrationStepRow
interface MigrationStepRow { migrationId: string; stepIndex: number; opKind: string; // 'addColumn' / 'dropTable' / … riskLevel: number; // 0–5 startedAt: Date; elapsedMs: number; outcome: 'ok' | 'failed' | 'skipped'; error?: string;}Snapshots — serializeSchemaGraph & friends
Each successful migration writes a serialized snapshot of the resulting graph to _zanith_schema_snapshots. The snapshot API lets you read those programmatically — for time-travel debugging, schema drift detection, or regenerating a graph as it existed at a point in time.
import { serializeSchemaGraph, deserializeSchemaGraph, listSnapshots, findSnapshot,} from 'zanith'; // List every snapshot:const all = await listSnapshots(adapter);// → SchemaSnapshotRow[] sorted by recordedAt DESC // Pull one back as a SchemaGraph:const row = await findSnapshot(adapter, migrationId);const past = deserializeSchemaGraph(row!.serialized); // Diff past vs current to see drift:const drift = diffSchemas(past, currentGraph);SchemaSnapshotRow
interface SchemaSnapshotRow { migrationId: string; phase: SnapshotPhase; // 'before' | 'after' | 'baseline' recordedAt: Date; serialized: SerializedSchemaGraph;}Artifacts — recovery surface as data
Soft-drops and archives leave a row in _zanith_artifacts. The artifact API lets you list, inspect, restore, export, and purge them — see Recovery for the workflow.
import { listArtifacts, findArtifact, readArtifactRows, restoreSoftDropColumn, restoreArchiveTable, purgeArtifact, purgeOlderThan, rowsToCsv, rowsToJsonLines,} from 'zanith'; const all = await listArtifacts(adapter);const one = await findArtifact(adapter, 'orders_total_softdrop'); // Stream the rows (returned as an async iterable):const rows = await readArtifactRows(adapter, one!.name);const csv = rowsToCsv(rows); // or rowsToJsonLines(rows) // Restore a soft-dropped column:await restoreSoftDropColumn(adapter, 'orders_total_softdrop'); // Or restore an archived table:await restoreArchiveTable(adapter, 'old_orders_archive_2026_03'); // Cleanup:await purgeArtifact(adapter, 'orders_total_softdrop');await purgeOlderThan(adapter, 30 * 24 * 60 * 60 * 1000);Shadow verification
import { verifyOnShadow } from 'zanith'; const report = await verifyOnShadow(adapter, plan, { shadowAdapter, // disposable adapter on a parallel DB // …other ShadowVerificationOptions}); if (!report.ok) { console.error(report.diff); // structural diff that survived application process.exit(1);}The narrative version of this step is at Shadow-DB verify; this is the function call that powers it.
When to use this instead of the CLI
| You want… | Reach for |
|---|---|
| A normal generate / verify / up flow. | The [CLI](/docs/reference/cli). Don't reinvent it. |
| A custom dashboard that lists pending migrations and per-step audit. | listAppliedMigrations + listMigrationSteps. |
| Drift detection in a monitoring pipeline. | findSnapshot + diffSchemas. |
| Recovery wired into your admin UI. | listArtifacts + readArtifactRows + restore*. |
| Embedding migration in a deploy script that's not a shell. | applyMigrations directly. |