Mermaid ER Syntax

Diagram2Code parses standard Mermaid erDiagram syntax, extended with %% comment directives that carry SQL metadata. Standard Mermaid tooling ignores these directives, so your diagrams remain valid everywhere.

Basic Structure

erDiagram
    ENTITY_NAME {
        type  column_name  [PK|FK|UK]  ["comment"]
    }
    ENTITY_A ||--o{ ENTITY_B : label

Column Definitions

Each column is defined as type name [key] ["comment"]. Key markers and comments are optional.

MarkerMeaningSQL effect
PKPrimary keyAuto-increment primary key (SERIAL / AUTO_INCREMENT / etc.)
FKForeign keyGenerates ALTER TABLE … ADD CONSTRAINT … FOREIGN KEY
UKUniqueUNIQUE constraint on column
PK, FKComposite PK member + FKPart of composite primary key and a foreign key

Type Mapping

Diagram typePostgreSQLMySQLSQLiteOracle
intINTEGERINTINTEGERNUMBER(10)
bigintBIGINTBIGINTINTEGERNUMBER(19)
stringTEXTVARCHAR(255)TEXTVARCHAR2(255)
textTEXTTEXTTEXTCLOB
booleanBOOLEANTINYINT(1)INTEGERNUMBER(1)
decimalNUMERICDECIMALREALNUMBER
floatREALFLOATREALFLOAT
timestampTIMESTAMPDATETIMETEXTTIMESTAMP
datetimeTIMESTAMPDATETIMETEXTTIMESTAMP
dateDATEDATETEXTDATE
uuidUUIDCHAR(36)TEXTCHAR(36)
jsonJSONBJSONTEXTCLOB
enumCREATE TYPE … AS ENUMENUM(…)TEXT CHECK (…)VARCHAR2 CHECK (…)

Relationships

Relationships use the pattern LEFT ||--o{ RIGHT : "label". The cardinality markers are:

MeaningLeftRight
Exactly one||||
Zero or one|oo|
One or more}||{
Zero or more}oo{

The line type (-- vs ..) indicates identifying vs non-identifying. Both generate the same SQL foreign key.

%% Directives

Directives are %% comment lines that begin with ::. They are processed by Diagram2Code and ignored by other Mermaid renderers.

::NN — NOT NULL

Place the directive on the line immediately above the target column.

Diagram
erDiagram
    USER {
        int id PK
        %% ::NN
        string email
        string bio
    }
SQL (PostgreSQL)
CREATE TABLE "user" (
    "id"    SERIAL PRIMARY KEY,
    "email" TEXT NOT NULL,
    "bio"   TEXT
);

::DEFAULT(value) — Default value

Diagram
erDiagram
    USER {
        int id PK
        %% ::DEFAULT(true), ::NN
        boolean active
        %% ::DEFAULT(0)
        int score
    }
SQL
"active" BOOLEAN NOT NULL DEFAULT true,
"score"  INTEGER DEFAULT 0

::CHECK(expr) — CHECK constraint

Diagram
erDiagram
    PRODUCT {
        int id PK
        %% ::CHECK(price > 0), ::NN
        decimal price
        %% ::CHECK(stock >= 0), ::DEFAULT(0)
        int stock
    }
SQL
"price" NUMERIC NOT NULL CHECK (price > 0),
"stock" INTEGER DEFAULT 0 CHECK (stock >= 0)

::ENUM(v1,v2,...) — Enum values

Diagram
erDiagram
    ORDER {
        int id PK
        %% ::ENUM(pending,shipped,delivered)
        enum status
    }
SQL (PostgreSQL)
CREATE TYPE "order_status_enum"
    AS ENUM('pending','shipped','delivered');

CREATE TABLE "order" (
    "id"     SERIAL PRIMARY KEY,
    "status" "order_status_enum"
);

::INDEX([col1, col2]) — Composite index

Diagram
erDiagram
    ORDER {
        int id PK
        int user_id FK
        timestamp created_at

        %% ::INDEX([user_id, created_at])
        %% ::INDEX([user_id], name: "idx_order_user")
    }
SQL
CREATE INDEX "idx_order_user_id_created_at"
    ON "order" ("user_id", "created_at");
CREATE INDEX "idx_order_user"
    ON "order" ("user_id");

::UNIQUE([col1, col2]) — Composite unique index

%% ::UNIQUE([order_id, product_id])
-- generates: CREATE UNIQUE INDEX "uq_order_item_order_id_product_id" ON …

::RELATIONSHIP[onDelete: X] — FK cascade actions

Place the directive on the line immediately before the relationship line. Supported actions: CASCADE, SET NULL, RESTRICT, NO ACTION, SET DEFAULT.

Diagram
erDiagram
    %% ::RELATIONSHIP[onDelete: CASCADE]
    USER ||--o{ ORDER : places
    %% ::RELATIONSHIP[onDelete: SET NULL]
    DEPT ||--o{ EMP : employs
SQL
FOREIGN KEY ("user_id")
    REFERENCES "user"("id")
    ON DELETE CASCADE;

FOREIGN KEY ("dept_id")
    REFERENCES "dept"("id")
    ON DELETE SET NULL;

%% ::USE-LABEL — Use display labels as table names

When placed before erDiagram, display labels (e.g. ORD["Sales Order"]) are used as the SQL table name (sales_order) instead of the entity identifier (ORD).

Block-level directives — @col::

Apply a directive to a named column anywhere in the entity block:

erDiagram
    USER {
        int    id    PK
        string email UK
        string phone UK

        %% @email::UK(name: "uq_user_email")
        %% @phone::UK(name: "uq_user_phone")
    }

Generates named UNIQUE constraints instead of the auto-named defaults.

Tip: Multiple directives can be stacked or combined with commas: %% ::NN, ::DEFAULT(active)