Skip to main content
In addition to its CLI, supaschema ships as a fully typed Node.js library. You can call every major capability — extraction, diffing, planning, rendering, checking, verifying, and migrating — programmatically from your own scripts, CI pipelines, or build tools. All functions are exported from the top-level supaschema package and are fully typed with TypeScript declarations.

Installation

npm install supaschema
supaschema requires Node.js 18 or later. The libpg-query native parser is bundled as an optional dependency and is supported on Linux x64/arm64 and macOS x64/arm64. On unsupported platforms, parser-dependent features will emit SUPA_PARSE_UNAVAILABLE.

Imports

All public functions and types are available from the top-level package entry point:
import {
  // Extraction
  extractSourceModel,
  extractCatalogModel,
  // Planning & rendering
  planSchemaDiff,
  renderMigration,
  renderMigrationSplit,
  // Static analysis & verification
  checkMigrationSql,
  verifyMigration,
  // Migrations management
  migrationsStatus,
  renderMigrationsStatus,
  syncMigrations,
  // Type & schema generation
  generateDatabaseTypes,
  generateZodSchemas,
  // Configuration
  loadConfig,
  defaultConfig,
  resolveConfig,
  supaschemaConfigSchema,
  // Auditing & corpus
  auditModel,
  renderAuditReport,
  runCorpus,
  renderCorpusReport,
  // Utilities
  resolveDatabaseUrl,
  resolveSupabaseLocalDatabaseUrl,
  parseLineage,
  latestLineage,
  lineageLine,
  runConfiguredValidators,
  // Database admin (advanced)
  applyMigrationSql,
  applySql,
  catalogFingerprint,
} from 'supaschema'
You can import only what you need — tree-shaking is supported.

Functions

extractSourceModel

Extracts a SchemaModel from any supported source prefix. This is the starting point for almost every supaschema workflow: parse your declarative schema files into an in-memory model before diffing or auditing. Signature
function extractSourceModel(
  source: string,
  options?: ExtractSourceOptions
): Promise<SchemaModel>
Parameters
ParameterTypeDescription
sourcestringA source specifier. Supported prefixes: dir: (local directory), git: (git ref), database: (live connection), dump: (SQL dump file), catalog: (saved snapshot JSON).
options.cwdstringWorking directory for resolving relative paths. Defaults to process.cwd().
options.configSupaschemaConfigOptional pre-loaded config. If omitted, supaschema loads config from cwd.
ReturnsPromise<SchemaModel> — the parsed schema model with a deterministic fingerprint. Example
import { extractSourceModel } from 'supaschema'

const model = await extractSourceModel('dir:supabase/schemas', {
  cwd: process.cwd(),
})

console.log(model.fingerprint) // deterministic hash of all tracked objects
console.log(Object.keys(model.objects).length) // total tracked object count
The fingerprint on a SchemaModel is a deterministic hash of all tracked object definitions. Two models with the same fingerprint are semantically identical — no migration is needed between them.

extractCatalogModel

Extracts a SchemaModel by reading the live pg_catalog and information_schema of a running PostgreSQL database. Use this as your fromModel when generating a migration to apply against a live target. Signature
function extractCatalogModel(
  databaseUrl: string,
  options?: ExtractCatalogOptions
): Promise<SchemaModel>
Parameters
ParameterTypeDescription
databaseUrlstringA postgresql:// connection string.
options.configSupaschemaConfigOptional pre-loaded config. Controls which schemas to extract and which roles to exclude from grant tracking.
options.schemasstring[]Override the schema list to extract. Takes precedence over config.
ReturnsPromise<SchemaModel> — a model representing the current live database state. Example
import { extractCatalogModel, loadConfig } from 'supaschema'

const config = await loadConfig(process.cwd())
const liveModel = await extractCatalogModel(
  'postgresql://postgres:postgres@localhost:54322/postgres',
  { config }
)

console.log(liveModel.fingerprint)

planSchemaDiff

Computes a MigrationPlan — an ordered list of operations needed to move from fromModel to toModel. The planner resolves dependencies, detects destructive changes, applies rename hints, and assigns each operation a kind and a safety classification. Signature
function planSchemaDiff(
  fromModel: SchemaModel,
  toModel: SchemaModel,
  config?: SupaschemaConfig
): MigrationPlan
Parameters
ParameterTypeDescription
fromModelSchemaModelThe baseline state (e.g., current live database or last migration endpoint).
toModelSchemaModelThe target state (e.g., your declarative schema files).
configSupaschemaConfigOptional config. Controls hints (destructive, renames), excluded roles, and planner behaviour.
ReturnsMigrationPlan — synchronous; no I/O required. Example
import { extractSourceModel, extractCatalogModel, planSchemaDiff } from 'supaschema'

const fromModel = await extractCatalogModel('postgresql://...')
const toModel = await extractSourceModel('dir:supabase/schemas')

const plan = planSchemaDiff(fromModel, toModel)

console.log(plan.operations.length)          // total number of changes
console.log(plan.operations[0].kind)         // 'create' | 'alter' | 'replace' | 'drop' | 'rename'
console.log(plan.operations[0].ref)          // e.g. 'public.users' (table)
console.log(plan.operations[0].destructive)  // true if this operation destroys data
console.log(plan.operations[0].blocked)      // true if a hint is required to proceed
If any operation in the plan has blocked: true, renderMigrationSplit will throw unless you resolve the blocking diagnostics (typically by adding entries to hints.destructive in your config).

renderMigration

Renders a MigrationPlan into a single SQL string. Use this when you want raw SQL output and will handle concurrent-statement splitting yourself, or when you know the plan contains no CONCURRENTLY statements. Signature
function renderMigration(
  plan: MigrationPlan,
  options?: RenderOptions
): string
Parameters
ParameterTypeDescription
planMigrationPlanA plan produced by planSchemaDiff.
options.configSupaschemaConfigOptional config.
options.includeHeaderbooleanIf true, prepend a supaschema header comment with lineage metadata. Defaults to true.
options.versionstringOptional version string to embed in the header.
Returnsstring — the full migration SQL. Example
import { planSchemaDiff, renderMigration } from 'supaschema'

const plan = planSchemaDiff(fromModel, toModel, config)
const sql = renderMigration(plan, { config })

console.log(sql)
Prefer renderMigrationSplit in most pipelines. It automatically separates CREATE INDEX CONCURRENTLY and REFRESH MATERIALIZED VIEW CONCURRENTLY statements into a non-transactional file so you never accidentally run them inside a transaction block.

renderMigrationSplit

Renders a MigrationPlan into SQL. Because some statements (e.g., CREATE INDEX CONCURRENTLY) cannot run inside a transaction block, renderMigrationSplit always returns two outputs: the main transactional migration and an optional concurrent-statements file. Signature
function renderMigrationSplit(
  plan: MigrationPlan,
  options?: RenderOptions
): RenderResult
Parameters
ParameterTypeDescription
planMigrationPlanA plan produced by planSchemaDiff.
options.configSupaschemaConfigOptional config.
ReturnsRenderResult
interface RenderResult {
  sql: string           // main migration SQL (transactional)
  concurrentSql?: string // separate file for CONCURRENTLY statements, if any
}
Example
import { renderMigrationSplit } from 'supaschema'
import { writeFileSync } from 'fs'

const { sql, concurrentSql } = renderMigrationSplit(plan, { config })

writeFileSync('supabase/migrations/20240101000000_changes.sql', sql)

if (concurrentSql) {
  // Apply this file outside a transaction block after the main migration
  writeFileSync('supabase/migrations/20240101000001_concurrent.sql', concurrentSql)
}
Apply concurrentSql after the main migration and outside a transaction block. The Supabase CLI handles this automatically when you use supaschema sync.

checkMigrationSql

Runs supaschema’s full static-analysis check suite against a raw SQL string. Returns an array of Diagnostic objects covering idempotency guards, lock hazards, security issues, and more. You can call this on any migration SQL — not just supaschema-generated output. Signature
function checkMigrationSql(
  sql: string,
  options?: CheckOptions
): Promise<Diagnostic[]>
Parameters
ParameterTypeDescription
sqlstringThe migration SQL to analyze.
options.configSupaschemaConfigOptional config. Controls which check rules are enabled.
options.filenamestringOptional filename for diagnostic messages.
ReturnsPromise<Diagnostic[]> — all diagnostics found, across all severity levels. Example
import { checkMigrationSql } from 'supaschema'
import { readFileSync } from 'fs'

const sql = readFileSync('supabase/migrations/20240101000000_changes.sql', 'utf8')
const diagnostics = await checkMigrationSql(sql, { config })

const errors = diagnostics.filter(d => d.severity === 'error')
const warnings = diagnostics.filter(d => d.severity === 'warning')

if (errors.length > 0) {
  console.error('Migration has errors:', errors)
  process.exit(1)
}

verifyMigration

Applies your migration to a throwaway database created from the from state, then checks whether the resulting catalog matches the to model. This catches missing operations, drift, and apply-twice failures before you ship to production. Signature
function verifyMigration(options: VerifyMigrationOptions): Promise<Diagnostic[]>
Parameters
ParameterTypeDescription
options.databaseUrlstringConnection URL for a PostgreSQL server. The connecting role must have CREATEDB.
options.fromstringSource specifier for the baseline state (e.g., 'git:HEAD', 'dir:supabase/schemas').
options.tostringSource specifier for the target state.
options.migrationPathstringPath to the migration SQL file to verify.
options.ensureRolesbooleanIf true, create any roles referenced in the migration that don’t exist in the throwaway database. Defaults to false.
options.configSupaschemaConfigOptional pre-loaded config.
ReturnsPromise<Diagnostic[]> — empty if verification passed; diagnostics otherwise. Example
import { verifyMigration } from 'supaschema'

const diagnostics = await verifyMigration({
  databaseUrl: 'postgresql://postgres:postgres@localhost:54322/postgres',
  from: 'git:HEAD',
  to: 'dir:supabase/schemas',
  migrationPath: './supabase/migrations/20240101000000_changes.sql',
  ensureRoles: true,
})

if (diagnostics.some(d => d.severity === 'error')) {
  console.error('Verification failed:', diagnostics)
  process.exit(1)
}

console.log('Migration verified successfully.')
verifyMigration creates and drops a temporary database on the target server. The connecting role must have CREATEDB. On Supabase, use supabase_admin or a custom role with CREATEDB.

migrationsStatus

Compares migration files on disk against the applied-history table in a target database. Returns a structured report of which migrations are pending, applied, ghost (in history but not on disk), or out-of-order. Signature
function migrationsStatus(options: MigrationsStatusOptions): Promise<MigrationsReport>
Parameters
ParameterTypeDescription
options.migrationsDirstringPath to the directory containing migration SQL files.
options.databaseUrlstringOptional. Connection URL for the target database. If omitted, returns a disk-only report and emits SUPA_MIGRATIONS_NO_TARGET.
options.historyTablestringOptional. Fully-qualified name of the history table (e.g., supabase_migrations.schema_migrations).
options.configSupaschemaConfigOptional pre-loaded config.
ReturnsPromise<MigrationsReport> Example
import { migrationsStatus } from 'supaschema'

const report = await migrationsStatus({
  migrationsDir: 'supabase/migrations',
  databaseUrl: 'postgresql://postgres:postgres@localhost:54322/postgres',
})

console.log(report.pending)     // migrations on disk not yet applied
console.log(report.applied)     // migrations recorded in the history table
console.log(report.ghost)       // history entries with no file on disk
console.log(report.outOfOrder)  // pending files older than the newest applied
console.log(report.diagnostics) // any SUPA_MIGRATIONS_* diagnostics

renderMigrationsStatus

Formats a MigrationsStatusReport (returned by migrationsStatus) into a human-readable string, suitable for printing to a terminal or writing to a log. Signature
function renderMigrationsStatus(report: MigrationsStatusReport): string
Returnsstring — formatted status output. Example
import { migrationsStatus, renderMigrationsStatus } from 'supaschema'

const report = await migrationsStatus({ migrationsDir: 'supabase/migrations' })
console.log(renderMigrationsStatus(report))

loadConfig

Loads and validates supaschema.config.json from a given directory, returning a fully typed SupaschemaConfig object. supaschema validates the config against its internal JSON schema and throws if the file is invalid. Signature
function loadConfig(
  cwd?: string,
  explicitPath?: string
): Promise<SupaschemaConfig>
Parameters
ParameterTypeDescription
cwdstringDirectory to search for supaschema.config.json. Defaults to process.cwd().
explicitPathstringOptional absolute or relative path to a config file, bypassing the search.
ReturnsPromise<SupaschemaConfig> — the validated config object. Example
import { loadConfig } from 'supaschema'

const config = await loadConfig(process.cwd())

console.log(config.schemaPaths)        // e.g. ['supabase/schemas']
console.log(config.hints?.destructive) // e.g. ['public.old_table']
console.log(config.excludedGrantRoles) // roles filtered from grant tracking

defaultConfig

The built-in default configuration object. You can spread this as a baseline when constructing a config programmatically.
import { defaultConfig } from 'supaschema'

console.log(defaultConfig.migrationsDir) // default migrations directory

resolveConfig

Merges a partial config with the defaults, producing a fully resolved SupaschemaConfig. Useful when you want to apply user-supplied overrides on top of the defaults without loading from disk. Signature
function resolveConfig(partial: Partial<SupaschemaConfig>): SupaschemaConfig

supaschemaConfigSchema

The Zod schema that supaschema uses to validate supaschema.config.json. Import this if you want to validate a config object in your own code.
import { supaschemaConfigSchema } from 'supaschema'

const result = supaschemaConfigSchema.safeParse(myConfig)
if (!result.success) {
  console.error(result.error.format())
}

auditModel

Audits a SchemaModel and returns a detailed report of which objects were extracted cleanly, which triggered warnings, and which were skipped. Equivalent to running npx supaschema audit from the CLI. Signature
function auditModel(
  model: SchemaModel,
  options?: AuditOptions
): AuditReport
Parameters
ParameterTypeDescription
modelSchemaModelA model returned by extractSourceModel or extractCatalogModel.
options.configSupaschemaConfigOptional pre-loaded config.
ReturnsAuditReport — synchronous. Example
import { extractSourceModel, auditModel } from 'supaschema'

const model = await extractSourceModel('dir:supabase/schemas')
const report = auditModel(model)

console.log(report.extracted.length) // cleanly tracked objects
console.log(report.warnings.length)  // objects with extraction warnings
console.log(report.skipped.length)   // untracked statements

renderAuditReport

Formats an AuditReport (returned by auditModel) into a human-readable string for terminal output. Signature
function renderAuditReport(report: AuditReport): string
Example
import { extractSourceModel, auditModel, renderAuditReport } from 'supaschema'

const model = await extractSourceModel('dir:supabase/schemas')
const report = auditModel(model)
console.log(renderAuditReport(report))

syncMigrations

Applies all pending migration files to the target database in order, recording each applied migration in the history table. Wraps the Supabase CLI runner and returns diagnostics from the run. Signature
function syncMigrations(options: SyncOptions): Promise<Diagnostic[]>
Parameters
ParameterTypeDescription
options.migrationsDirstringPath to the migrations directory.
options.databaseUrlstringConnection URL for the target database.
options.historyTablestringOptional. Fully-qualified history table name.
options.configSupaschemaConfigOptional pre-loaded config.
ReturnsPromise<Diagnostic[]> — includes SUPA_SYNC_RUNNER_FAILED if the runner exits nonzero.

generateDatabaseTypes

Generates TypeScript type definitions from a SchemaModel or live database, suitable for use with the Supabase client SDK. Signature
function generateDatabaseTypes(
  source: string | SchemaModel,
  options?: GenerateTypesOptions
): Promise<string>
ReturnsPromise<string> — TypeScript source code.

generateZodSchemas

Generates Zod schema definitions from a SchemaModel, covering all tables and their columns with appropriate types and nullability. Signature
function generateZodSchemas(
  source: string | SchemaModel,
  options?: GenerateZodOptions
): Promise<string>
ReturnsPromise<string> — TypeScript source code with Zod schemas.

runCorpus

Runs the corpus oracle against a set of schema sources to verify that supaschema’s render pipeline converges — i.e., that applying the generated migration produces exactly the declared target state. Primarily used in CI pipelines to catch render regressions. Signature
function runCorpus(options: CorpusOptions): Promise<CorpusReport>
ReturnsPromise<CorpusReport> — the full corpus run report including any SUPA_CORPUS_RECONVERGENCE diagnostics.

renderCorpusReport

Formats a CorpusReport into a human-readable string. Signature
function renderCorpusReport(report: CorpusReport): string

resolveDatabaseUrl

Resolves a database URL from explicit input, environment variables, or project configuration. Falls back through DATABASE_URL, SUPABASE_DB_URL, and local Supabase defaults in order. Signature
function resolveDatabaseUrl(
  explicit?: string,
  options?: { cwd?: string }
): Promise<string | undefined>

resolveSupabaseLocalDatabaseUrl

Resolves the database URL for a locally running Supabase stack (via supabase start). Reads from .supabase/config.toml or falls back to the standard local default (postgresql://postgres:postgres@localhost:54322/postgres). Signature
function resolveSupabaseLocalDatabaseUrl(cwd?: string): Promise<string | undefined>

parseLineage

Parses the lineage header embedded in a supaschema migration file and returns a structured MigrationLineage object containing the from and to fingerprints. Signature
function parseLineage(sql: string): MigrationLineage | undefined

latestLineage

Scans a migrations directory and returns the MigrationLineage from the most recently written supaschema migration file (by filename sort order). Signature
function latestLineage(migrationsDir: string): Promise<MigrationLineage | undefined>

lineageLine

Returns the canonical lineage header string for embedding in a migration file, given from and to fingerprints. Signature
function lineageLine(from: string, to: string): string

runConfiguredValidators

Runs all external SQL validators listed in supaschema.config.json against a SQL string and returns a combined array of Diagnostic objects. Emits SUPA_VALIDATOR_UNAVAILABLE if a configured validator is not installed, and SUPA_VALIDATOR_FAILED if one reports issues. Signature
function runConfiguredValidators(
  sql: string,
  config: SupaschemaConfig,
  options?: { filename?: string }
): Promise<Diagnostic[]>

applyMigrationSql (db-admin)

Applies a SQL migration string to a live database connection. Low-level utility used internally by syncMigrations and verifyMigration. Use with caution in application code. Signature
function applyMigrationSql(
  databaseUrl: string,
  sql: string
): Promise<void>

applySql (db-admin)

Executes an arbitrary SQL string against a live database connection. Low-level utility for administrative scripts. Signature
function applySql(
  databaseUrl: string,
  sql: string
): Promise<void>

catalogFingerprint (db-admin)

Extracts and returns the current fingerprint of the live database catalog — the same deterministic hash that extractCatalogModel embeds in the SchemaModel. Use this to cheaply check whether a database’s schema has changed without extracting the full model. Signature
function catalogFingerprint(
  databaseUrl: string,
  options?: { config?: SupaschemaConfig }
): Promise<string>

Key TypeScript types

All types are exported from the supaschema package.

SchemaModel

A parsed snapshot of all tracked schema objects, with a deterministic fingerprint.
interface SchemaModel {
  /** The source specifier used to produce this model. */
  source: string

  /** All tracked objects, in dependency order. */
  objects: SchemaObject[]

  /** Raw diagnostics emitted during extraction. */
  diagnostics: Diagnostic[]

  /** Deterministic hash of all tracked object definitions. */
  fingerprint: string

  /** Internal format version; used to detect stale snapshots. */
  formatVersion?: number
}

MigrationPlan

The output of planSchemaDiff — an ordered list of operations with from/to fingerprints.
interface MigrationPlan {
  /** Fingerprint of the baseline (from) model. */
  fromFingerprint: string

  /** Fingerprint of the target (to) model. */
  toFingerprint: string

  /** Ordered list of operations to apply. */
  operations: MigrationOperation[]

  /** Diagnostics emitted during planning. */
  diagnostics: Diagnostic[]
}

MigrationOperation

A single change within a MigrationPlan.
interface MigrationOperation {
  /** The type of change. */
  kind: MigrationOperationKind // 'alter' | 'create' | 'replace' | 'drop' | 'rename'

  /** Structured reference to the affected object (kind, name, schema). */
  ref: ObjectRef

  /** Fully-qualified identity key (e.g. 'public.users'). */
  key: string

  /** The object's state before the operation (undefined for creates). */
  before?: SchemaObject

  /** The object's state after the operation (undefined for drops). */
  after?: SchemaObject

  /** True if this operation permanently destroys data or objects. */
  destructive: boolean

  /**
   * True if supaschema requires a hint before it will render this operation.
   * Blocked operations cause `renderMigration` / `renderMigrationSplit` to throw.
   */
  blocked: boolean

  /** Diagnostics emitted for this specific operation during planning. */
  diagnostics: Diagnostic[]

  /** Additional planner metadata (internal use). */
  metadata: Record<string, unknown>
}

Diagnostic

A structured diagnostic emitted by any supaschema function.
interface Diagnostic {
  /** Stable diagnostic code (e.g. 'SUPA_PLAN_DESTRUCTIVE_HINT_REQUIRED'). */
  code: string

  /** Severity level. */
  severity: DiagnosticSeverity // 'error' | 'warning' | 'info'

  /** Human-readable explanation of the issue. */
  message: string

  /** Structured reference to the affected object. */
  ref?: ObjectRef

  /** Path to the source file where the issue was detected. */
  file?: string

  /** The SQL statement that triggered the diagnostic, if applicable. */
  statement?: string

  /** Actionable suggestion for resolving the issue. */
  hint?: string

  /** Schemas referenced by the failing statement (used for schema-filter scoping). */
  schemas?: string[]
}

SupaschemaConfig

The full configuration type, matching the shape of supaschema.config.json.
interface SupaschemaConfig {
  /** Schema source paths (e.g. ['supabase/schemas']). */
  schemaPaths: string[]

  /** Migrations output directory. */
  migrationsDir?: string

  /** Roles to exclude from grant tracking. */
  excludedGrantRoles?: string[]

  /** Hints that influence planning behavior. */
  hints?: {
    /** Object keys that are allowed to be dropped or destructively altered. */
    destructive?: string[]

    /** Rename hints, each specifying the old and new fully-qualified object key. */
    renames?: RenameHint[]
  }

  /** External SQL validators to run after check. */
  validators?: string[]

  /** History table for migrations tracking. */
  historyTable?: string
}
The authoritative shape of SupaschemaConfig is defined by supaschemaConfigSchema (a Zod schema exported from supaschema). Use supaschemaConfigSchema.safeParse(obj) to validate a config object at runtime.

RenameHint

A rename instruction passed via hints.renames in config, mapping an old fully-qualified object key to a new one.
interface RenameHint {
  from: string // e.g. 'public.old_table_name'
  to: string   // e.g. 'public.new_table_name'
}

ObjectRef

A structured reference to a named database object, attached to operations and diagnostics.
interface ObjectRef {
  kind: ObjectKind
  name: string
  schema?: string
  /** Function/procedure parameter-type signature, if applicable. */
  signature?: string
  /** Parent table name, for constraints, indexes, triggers, and policies. */
  table?: string
}

DiagnosticSeverity, ObjectKind, and MigrationOperationKind

Convenience type aliases exported from supaschema:
type DiagnosticSeverity = 'info' | 'warning' | 'error'

type ObjectKind =
  | 'schema' | 'extension' | 'type' | 'domain' | 'enum' | 'sequence'
  | 'table' | 'foreign-data-wrapper' | 'foreign-server' | 'foreign-table'
  | 'constraint' | 'index' | 'function' | 'procedure'
  | 'view' | 'materialized-view' | 'trigger'
  | 'rls' | 'policy' | 'grant' | 'default-privilege' | 'comment'

type MigrationOperationKind = 'alter' | 'create' | 'replace' | 'drop' | 'rename'