Schema Diff

Last updated: March 2026

The Schema Diff API lets you compare two ER diagrams — an old version and a new version — and receive a structured JSON list of every schema change. It supports Mermaid and PlantUML, including cross-format comparisons. The endpoint is free and requires no authentication.

Use Schema Diff to understand what changed between two diagram revisions, power code review tools, or build change-log generators. If you need the actual SQL ALTER TABLE / CREATE TABLE statements to apply the diff to a live database, see POST /v1/convert/migration.

Interactive UI

The easiest way to use Schema Diff is the Diff page. Paste your old diagram on the left and your new diagram on the right, select the format for each panel, and click Compare Schemas. Results are colour-coded immediately in the browser.

A Destructive badge marks changes that could cause data loss — dropped tables or columns, type changes, and NOT NULL additions without a default value.

POST /v1/diff

Compare two diagrams and receive a structured JSON diff. No API key required.

POST https://diagram2code.com/v1/diff
Content-Type: application/json

Request Fields

FieldTypeRequiredDescription
oldDiagramTypestringYesFormat of the baseline diagram: "mermaid" or "plantuml"
oldDiagramstringYesThe baseline (old) diagram text
newDiagramTypestringYesFormat of the updated diagram: "mermaid" or "plantuml"
newDiagramstringYesThe updated (new) diagram text
Request body
{
  "oldDiagramType": "mermaid",
  "oldDiagram": "erDiagram\n    USER {\n        int id PK\n        string name\n    }\n",
  "newDiagramType": "mermaid",
  "newDiagram": "erDiagram\n    USER {\n        int id PK\n        string name\n        string email\n    }\n    ORDER {\n        int id PK\n        int user_id FK\n        decimal total\n    }\n    USER ||--o{ ORDER : places\n"
}

Response Fields

A successful request returns HTTP 200 OK with the following JSON body.

FieldTypeDescription
changesarrayOrdered list of schema changes. Empty when the two diagrams are schema-equivalent.
changes[].opstringThe change operation — see Change Operations.
changes[].tablestringThe table affected by this change.
changes[].columnstringThe column affected (omitted for table-level operations).
changes[].descriptionstringHuman-readable description of the change.
changes[].destructivebooleantrue when the change may cause data loss.
summary.totalintegerTotal number of changes.
summary.additionsintegerNumber of additive changes (CREATE_TABLE, ADD_COLUMN, ADD_INDEX, ADD_FK).
summary.deletionsintegerNumber of removal changes (DROP_TABLE, DROP_COLUMN, DROP_INDEX, DROP_FK).
summary.modificationsintegerNumber of modifications (ALTER_COLUMN).
summary.destructiveCountintegerNumber of changes where destructive is true.
Response body
{
  "changes": [
    {
      "op": "ADD_COLUMN",
      "table": "USER",
      "column": "email",
      "description": "Added column \"email\" to table \"USER\"",
      "destructive": false
    },
    {
      "op": "CREATE_TABLE",
      "table": "ORDER",
      "description": "Created table \"ORDER\"",
      "destructive": false
    },
    {
      "op": "ADD_FK",
      "table": "ORDER",
      "description": "Added foreign key on \"ORDER\" referencing \"USER\"",
      "destructive": false
    }
  ],
  "summary": {
    "total": 3,
    "additions": 3,
    "deletions": 0,
    "modifications": 0,
    "destructiveCount": 0
  }
}

Change Operations

The op field in each change entry is one of the following constants.

OpKindDestructive?Description
CREATE_TABLEadditionNoA new table was added in the new diagram
DROP_TABLEdeletionAlwaysA table present in the old diagram is missing from the new diagram
ADD_COLUMNadditionNoA new column was added to an existing table
DROP_COLUMNdeletionAlwaysA column was removed from an existing table
ALTER_COLUMNmodificationSometimesA column's type, nullability, or default value changed. Destructive when the type changes, or when NOT NULL is added without a default value.
ADD_INDEXadditionNoA new index was added
DROP_INDEXdeletionNoAn index was removed
ADD_FKadditionNoA foreign key relationship was added
DROP_FKdeletionNoA foreign key relationship was removed

Curl Examples

1. Add a column

curl -s https://diagram2code.com/v1/diff \
  -H 'Content-Type: application/json' \
  -d '{
    "oldDiagramType": "mermaid",
    "oldDiagram": "erDiagram\n    USER {\n        int id PK\n        string name\n    }\n",
    "newDiagramType": "mermaid",
    "newDiagram": "erDiagram\n    USER {\n        int id PK\n        string name\n        string email\n    }\n"
  }'

2. Add a table with a foreign key

curl -s https://diagram2code.com/v1/diff \
  -H 'Content-Type: application/json' \
  -d '{
    "oldDiagramType": "mermaid",
    "oldDiagram": "erDiagram\n    USER { int id PK\n string name }\n",
    "newDiagramType": "mermaid",
    "newDiagram": "erDiagram\n    USER { int id PK\n string name }\n    ORDER { int id PK\n int user_id FK }\n    USER ||--o{ ORDER : places\n"
  }'

3. Drop a column (destructive)

curl -s https://diagram2code.com/v1/diff \
  -H 'Content-Type: application/json' \
  -d '{
    "oldDiagramType": "mermaid",
    "oldDiagram": "erDiagram\n    USER {\n        int id PK\n        string name\n        string legacy_field\n    }\n",
    "newDiagramType": "mermaid",
    "newDiagram": "erDiagram\n    USER {\n        int id PK\n        string name\n    }\n"
  }'

4. Cross-format diff (Mermaid → PlantUML)

You can compare diagrams in different formats. The two diagrams are parsed independently then diffed on their normalised schema representations, so the source format is irrelevant.

curl -s https://diagram2code.com/v1/diff \
  -H 'Content-Type: application/json' \
  -d '{
    "oldDiagramType": "mermaid",
    "oldDiagram": "erDiagram\n    USER { int id PK\n string name }\n",
    "newDiagramType": "plantuml",
    "newDiagram": "@startuml\nentity USER {\n  * id : int <<PK>>\n  name : string\n  email : string\n}\n@enduml"
  }'

5. Identical diagrams

When the two diagrams describe the same schema (after normalisation), the response will have an empty changes array and all summary counts will be zero.

curl -s https://diagram2code.com/v1/diff \
  -H 'Content-Type: application/json' \
  -d '{
    "oldDiagramType": "mermaid",
    "oldDiagram": "erDiagram\n    USER { int id PK }\n",
    "newDiagramType": "mermaid",
    "newDiagram": "erDiagram\n    USER { int id PK }\n"
  }'

Destructive Changes

A change is marked "destructive": true when applying it to a live database could cause permanent data loss. The following operations are always destructive:

The following are conditionally destructive:

These are never destructive:

Use summary.destructiveCount to gate CI checks or PR comments when any destructive change is present.

Diff vs Migration SQL

Schema Diff (POST /v1/diff) returns a human-readable, machine-parseable description of what changed. It is free, requires no authentication, and produces no SQL.

Migration SQL (POST /v1/convert/migration) generates the actual ALTER TABLE, CREATE TABLE, and DROP statements needed to evolve a live database from the old schema to the new one. It is dialect-aware (PostgreSQL, MySQL, SQLite, Oracle) and requires a Developer or higher plan.

POST /v1/diffPOST /v1/convert/migration
OutputStructured JSON change listSQL DDL migration script
Free tierYes — no API key requiredNo — Developer plan required
SQL dialectNot applicablepostgres, mysql, sqlite, oracle
Warnings for destructive opsVia destructive: true flagVia SQL -- WARNING: comments
Cross-formatYesYes

Troubleshooting

INPUT_ERROR — "oldDiagram is required"

All four fields (oldDiagramType, oldDiagram, newDiagramType, newDiagram) must be present and non-empty. If any field is missing or blank the server returns HTTP 400 with "code": "INPUT_ERROR".

INPUT_ERROR — "unsupported diagram type"

The only accepted values for oldDiagramType and newDiagramType are "mermaid" and "plantuml" (lowercase). Any other value — including "graphviz", "dbml", or an empty string — returns a 400 error.

PARSE_ERROR — "expected erDiagram keyword"

Every Mermaid diagram must begin with the literal keyword erDiagram. If your diagram starts with a comment, a blank line, or a different Mermaid diagram type, the parser will reject it. Strip any leading whitespace or %% comment lines before sending.

PARSE_ERROR with PlantUML

PlantUML diagrams must start with @startuml and end with @enduml. Entity blocks use the entity keyword. Attribute markers like * (mandatory) and <<PK>> (primary key) are optional but must be well-formed if present. See PlantUML Syntax for the full grammar reference.

Changes array is empty but I made changes

Both schemas are normalised before diffing. Normalisation injects a synthetic primary key column if no PK is defined, and it may rename or deduplicate columns. If normalisation produces an equivalent schema from both diagrams, the diff will be empty. To verify, try the interactive Diff UI and inspect the rendered change list. If the diagrams are semantically identical after normalisation, the empty result is correct.

Cross-format diff shows unexpected DROP + CREATE instead of ADD_COLUMN

The diff algorithm is case-sensitive on table names. If your Mermaid diagram has USER (uppercase) and your PlantUML diagram has entity User (title case), they are treated as different tables. Use the same casing for table names across both diagrams to get accurate column-level diffs.