Convert Mermaid ER to SQLite

Generate production-ready SQLite SQL code from your Mermaid ER diagrams in seconds.

sqlite
Dialect: sqliteTables: 2
Done.
mermaid
Live preview mermaid

How the Mermaid ER to SQLite conversion works

SQLite is the most constrained SQL target — no ALTER TABLE … ADD CONSTRAINT, no enum type, no fixed-point decimals — and the generator leans into those limits instead of fighting them: foreign keys go inline, enums become CHECK constraints, and INTEGER PRIMARY KEY aliases the rowid so auto-increment needs no keyword at all.

Mermaid erDiagram is a Markdown-friendly notation rendered natively by GitHub, GitLab, Notion, and Obsidian — which makes it the most common way to keep an ER diagram next to the code it describes.

Entities are blocks of `type name marker` lines (markers: PK, FK, UK, INDEX), and relationships use cardinality arrows like `USER ||--o{ POST : writes` — one-to-many from USER to POST. The converter reads both, plus comment directives for everything the base syntax can't express.

Paste your Mermaid ER source into the editor above and the engine parses it into an intermediate schema — tables, columns, relationships, indexes — then generates SQLite DDL from that model. Anything the diagram syntax can't express (NOT NULL, defaults, enums, CHECK constraints, composite indexes) you add with comment directives like %% ::NN, ::DEFAULT(now()) on the line above a column. The Mermaid ER reference documents the full vocabulary.

Worked example: an e-commerce schema

A complete Mermaid ER diagram and the exact SQLite output the generator produces for it — different from the starter diagram in the editor above, so you can see a second real conversion end to end:

erDiagram
    CUSTOMER ||--o{ ORDER : places
    PRODUCT ||--o{ ORDER : "ordered in"
    CUSTOMER {
        int id PK
        string email UK
        %% ::NN
        string name
        %% ::DEFAULT(now())
        timestamp created_at
    }
    PRODUCT {
        int id PK
        string sku UK
        %% ::CHECK(price > 0)
        decimal price
        %% ::ENUM(draft, active, retired)
        string status
    }
    ORDER {
        int id PK
        int customer_id FK
        int product_id FK
        %% ::DEFAULT(0)
        decimal total
    }
CREATE TABLE "customer" (
    "id" INTEGER PRIMARY KEY,
    "email" TEXT UNIQUE,
    "name" TEXT NOT NULL,
    "created_at" TEXT DEFAULT now()
);

CREATE TABLE "order" (
    "id" INTEGER PRIMARY KEY,
    "customer_id" INTEGER REFERENCES "customer"("id"),
    "product_id" INTEGER REFERENCES "product"("id"),
    "total" REAL DEFAULT 0
);

CREATE TABLE "product" (
    "id" INTEGER PRIMARY KEY,
    "sku" TEXT UNIQUE,
    "price" REAL CHECK (price > 0),
    "status" TEXT CHECK ("status" IN ('draft', 'active', 'retired'))
);

Compare this to the PostgreSQL output from the same diagram: there are no ALTER TABLE statements. SQLite can't add constraints after creation, so customer_id and product_id carry inline REFERENCES clauses instead, and the converter orders the CREATE TABLE statements so parents exist first. The enum collapsed to TEXT with a CHECK (status IN (…)), and decimal became REAL — SQLite has no fixed-point type.

What the generator emits for SQLite

  • CREATE TABLE statements using SQLite type affinity
  • inline REFERENCES clauses for foreign keys (SQLite cannot ADD CONSTRAINT later)
  • CHECK (col IN (…)) constraints for ::ENUM(...) columns
  • INTEGER primary keys that alias SQLite's rowid
  • CREATE INDEX / CREATE UNIQUE INDEX, including partial indexes with a WHERE clause

Type mapping: Mermaid ER to SQLite

Diagram column types are logical — the generator maps each one to the idiomatic SQLite type. These are the exact mappings the engine uses:

Diagram typeSQLite typeNotes
intINTEGERan INTEGER PRIMARY KEY aliases the rowid
stringTEXTSQLite ignores varchar lengths; affinity is TEXT
textTEXT
decimalREALSQLite has no fixed-point type
booleanINTEGER0 / 1 by convention
dateTEXTISO-8601 strings, per SQLite best practice
timestampTEXTISO-8601 strings
uuidTEXT
bigintINTEGERSQLite INTEGER is 8 bytes
blobBLOB

Keys, relationships, and enums

A single `int id PK` becomes `"id" INTEGER PRIMARY KEY`, which in SQLite aliases the internal rowid and auto-assigns values — no AUTOINCREMENT keyword needed for the common case.

SQLite cannot add constraints after table creation, so foreign keys are emitted inline as `REFERENCES parent("id")` on the column. The converter orders tables so parents are created before children. Remember to run PRAGMA foreign_keys = ON.

SQLite has no enum type, so `::ENUM(active, inactive)` emits `TEXT CHECK ("status" IN ('active', 'inactive'))` — the values are still enforced, just via a CHECK constraint instead of a type.

Frequently asked questions

Where did the foreign key constraints go?

They're inline: REFERENCES "customer"("id") sits directly on the column because SQLite doesn't support ALTER TABLE … ADD CONSTRAINT. Remember to run PRAGMA foreign_keys = ON per connection — SQLite ships with enforcement off.

Is anything lost converting the same Mermaid diagram to SQLite instead of PostgreSQL?

The constraints survive in adapted form (enum → CHECK, decimal → REAL, timestamps → ISO-8601 TEXT), but you lose native enum types and TIMESTAMPTZ, which SQLite simply doesn't have. The diagram stays the single source; only the dialect rendering changes.

Why are foreign keys inline instead of ALTER TABLE statements?

SQLite does not support ALTER TABLE … ADD CONSTRAINT. The converter knows this and emits inline REFERENCES clauses, ordering the CREATE TABLE statements so referenced tables come first.

What happens to enum columns in SQLite?

They become TEXT with a CHECK (col IN (…)) constraint, so invalid values are still rejected even though SQLite has no native enum type.

Related conversions