zanith

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

TS
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]);
ExportPurpose
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.

TS
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

TS
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.

TS
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

TS
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.

TS
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

TS
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.