npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

dbml-diff

v0.7.0

Published

Structural diff for DBML schema files - emits text, JSON, or an annotated DBML document that renders as a visual diff in dbdiagram.io

Readme

dbml-diff

npm version CI license

Structurally diff two DBML schema files and emit the result as text, JSON, or an annotated DBML document that renders as a visual diff in dbdiagram.io.

No install needed:

npx dbml-diff old.dbml new.dbml                             # readable text summary
npx dbml-diff old.dbml new.dbml --format dbml -o diff.dbml  # visual diff, paste into dbdiagram.io

The visual diff shows only what changed - added, removed, and modified tables, with per-column annotations:

A rendered schema diff: tables prefixed NEW/MOD/DEL, changed columns suffixed and annotated

Why

If you keep your database schema as DBML in version control, git diff between two versions is line-noise: attribute reordering, whitespace, and hundreds of unchanged lines drown the handful of real changes. dbdiagram.io has no built-in version compare, and existing schema-diff tools target live databases, not DBML files. dbml-diff compares the two documents structurally - tables, columns, types, nullability, primary keys, enums - and tells you exactly what changed. Related upstream issue: holistics/dbml#175.

Install

npm i -g dbml-diff    # or keep using npx

CLI usage

dbml-diff <old.dbml> <new.dbml> [options]

Options:
  --format <text|json|dbml>   output format (default: text)
  --full-new-tables           in dbml format, emit full column lists for
                              added tables (default: stub to PK + note with
                              column count, because full definitions drown
                              the diagram at scale)
  --colors                    in dbml format, use headercolor annotations
                              (#2ecc71 added / #f39c12 modified / #e74c3c
                              removed) instead of relying on name prefixes
                              alone. Requires dbdiagram paid tier to render;
                              name prefixes are always emitted regardless.
  --hide-unchanged-pk         in dbml format, drop the unchanged primary-key
                              row from modified tables (leaner delta-only view)
  --migrate                   emit a T-SQL migration script (ALTER/CREATE DDL)
                              instead of a diff; DROP and heuristic RENAME
                              statements are commented out. Cannot be combined
                              with --format. Honors -o.
  --include-notes             treat a changed column note as a column change
                              (reported as "note changed"); off by default
  -o, --output <file>         write to file instead of stdout
  -h, --help                  usage
  --version                   package version

The counts summary (added: N, removed: N, modified: N) always goes to stderr, so stdout stays pipeable.

Visual diff conventions (--format dbml)

| Marker | Meaning | | --- | --- | | NEW · table name prefix | Table added | | MOD · table name prefix | Table modified | | DEL · table name prefix | Table removed | | __ADDED column suffix | Column added to a modified table | | __REMOVED column suffix | Column removed from a modified table | | __RENAMED column suffix | Rename candidate (heuristic - verify; never merged silently) | | __CHANGED column suffix | Type, nullability or PK membership changed (detail in the column note) | | NEW · / MOD · / DEL · enum name prefix | Enum added / modified / removed | | [note: 'ADDED'] / [note: 'REMOVED'] on an enum value | Value added / removed in a modified enum |

Modified tables show only their primary key (annotated unchanged columns omitted) plus the changed columns; --hide-unchanged-pk drops that PK row for a leaner delta-only view (the block stays valid because a modified table always has at least one changed column). Added tables are stubbed to the PK with a NEW TABLE - N columns note by default (--full-new-tables emits everything); removed tables are emitted in full. Enum changes are emitted as Enum blocks under the same NEW · / MOD · / DEL · prefixes; in a modified enum the full new value list is shown with ADDED notes on new values and the dropped values re-listed with REMOVED notes. A DIFF SUMMARY table at the top lists the counts: one column per metric, with the label as the column name and the count as the column type, so the numbers are visible on the canvas at a glance. It's a real table (not a standalone Note block) because dbdiagram renders standalone notes as Sticky Notes only on paid tiers, whereas a table always renders on the free tier. The three table counts (added / removed / modified) always appear; enum, ref, and TableGroup rows appear only for categories that changed.

Relationship (Ref:) changes are counted in the DIFF SUMMARY table (added, removed, retargeted when an FK side keeps its columns but points at a new parent, and unresolved for a change that cannot be mapped to a single retarget). The per-ref and per-group detail - which tables changed and how - lives in --format text and --format json, not in the diagram.

Viewing the diff in dbdiagram.io

  1. dbml-diff old.dbml new.dbml --format dbml -o diff.dbml
  2. Open dbdiagram.io and create a new diagram.
  3. Paste the contents of diff.dbml into the editor.
  4. The diagram now shows only what changed: scan for the NEW · / MOD · / DEL · tables, and hover the annotated columns to read the change notes. With --colors (paid tier) the table headers are colour-coded too.

Migration script (--migrate)

--migrate emits a T-SQL migration script (SQL Server / Azure Synapse) that transforms a database from the old schema to the new one, instead of a diff:

dbml-diff old.dbml new.dbml --migrate -o up.sql

What it emits live (uncommented):

  • Added tables become CREATE TABLE (with a PK_<table> constraint when the table has a primary key).
  • Added columns become ALTER TABLE ... ADD.
  • Type or nullability changes become ALTER TABLE ... ALTER COLUMN (the full target type is restated, as T-SQL requires).
  • Added foreign keys become ALTER TABLE ... ADD CONSTRAINT ... FOREIGN KEY (with an inline -- NOTE that it fails if existing rows violate the constraint).

For safety, destructive and heuristic statements are emitted commented out, so a pasted script cannot cause data loss on a straight run - review and uncomment them deliberately:

  • Removed tables (-- DROP TABLE ...) and removed columns (-- ALTER TABLE ... DROP COLUMN ...).
  • Rename candidates (-- EXEC sp_rename ...), which are heuristic - verify before running.
  • Removed foreign keys, and the old side of a retargeted foreign key (-- ALTER TABLE ... DROP CONSTRAINT ...). A retargeted key comments the old drop and emits the new ADD CONSTRAINT live.
  • Ambiguous (unresolved) ref changes, emitted as an -- UNRESOLVED ref change comment for you to resolve by hand.

Caveats:

  • Adding a NOT NULL column to a table that already has rows fails without a default, and tightening an existing column to NOT NULL fails if it holds any NULLs; both carry an inline -- NOTE.
  • Enums and TableGroups are not represented in the SQL output.
  • The generated FK constraint name (FK_<child>_<parent>_<childCols>) is synthesized and unqualified, so it will usually differ from the real constraint name in your database. Adjust the name on any DROP CONSTRAINT line before uncommenting it.
  • --migrate cannot be combined with --format, and T-SQL is currently the only dialect.

Programmatic API

const { diff, emitText, emitJson, emitDbml, emitMigration } = require('dbml-diff');

const result = diff(oldDbmlString, newDbmlString);
// {
//   tables: {
//     added:    [ { name, columns: [...] } ],
//     removed:  [ { name, columns: [...] } ],
//     modified: [ {
//       name,
//       columnsAdded: [...], columnsRemoved: [...],
//       columnsChanged: [ { column, changes: ['type X -> Y', ...] } ],
//       renames: [ { from, to } ]   // heuristic candidates only
//     } ]
//   },
//   enums: {
//     added:    [ { name, values: [...] } ],
//     removed:  [ { name, values: [...] } ],
//     modified: [ { name, values: [...], valuesAdded: [...], valuesRemoved: [...] } ]
//   },
//   refs: {
//     added:      [ { from: { table, columns }, to: { table, columns } } ],
//     removed:    [ { from, to } ],
//     retargeted: [ { from, oldTo, newTo } ],   // same FK side, new parent
//     unresolved: [ { from, oldTargets: [...], newTargets: [...] } ]  // ambiguous
//   },
//   groups: {   // TableGroups (membership diffed as a set)
//     added:    [ { name, tables: [...] } ],
//     removed:  [ { name, tables: [...] } ],
//     modified: [ { name, tablesAdded: [...], tablesRemoved: [...] } ]
//   },
//   counts: { added, removed, modified }   // tables only
// }

console.log(emitText(result));
console.log(emitJson(result));
console.log(emitDbml(result, { oldLabel: 'v1', newLabel: 'v2', colors: true }));
console.log(emitMigration(result, { oldLabel: 'v1', newLabel: 'v2' })); // T-SQL migration script

Exit codes

Useful for CI gates ("fail the build if the schema changed"):

| Exit code | Meaning | | --- | --- | | 0 | Schemas are identical | | 1 | Differences found | | 2 | Error (bad arguments, unreadable file, DBML parse failure) |

Parsing behaviour

  • dbdiagram-specific DiagramView top-level blocks are stripped before parsing (@dbml/core rejects them). TableGroup blocks are kept and diffed for group-membership changes.
  • Primary keys are detected from inline [pk] attributes and from Indexes { Col [pk] } blocks.
  • Schema-qualified table names (e.g. dbo.Shipments) are preserved as-is.

Roadmap

Visual public roadmap - what shipped, what's in progress, what's next. Generated from the issue tracker: issues labelled roadmap become cards, status: labels set the column, closed issues land in Done.

  • --migrate T-SQL migration script (CREATE/ALTER, foreign-key constraints, commented DROP/rename) - the ALTER-generation ask in upstream holistics/dbml#175.

License

Apache-2.0.