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.
| Marker | Meaning | SQL effect |
|---|---|---|
PK | Primary key | Auto-increment primary key (SERIAL / AUTO_INCREMENT / etc.) |
FK | Foreign key | Generates ALTER TABLE … ADD CONSTRAINT … FOREIGN KEY |
UK | Unique | UNIQUE constraint on column |
PK, FK | Composite PK member + FK | Part of composite primary key and a foreign key |
Type Mapping
| Diagram type | PostgreSQL | MySQL | SQLite | Oracle |
|---|---|---|---|---|
int | INTEGER | INT | INTEGER | NUMBER(10) |
bigint | BIGINT | BIGINT | INTEGER | NUMBER(19) |
string | TEXT | VARCHAR(255) | TEXT | VARCHAR2(255) |
text | TEXT | TEXT | TEXT | CLOB |
boolean | BOOLEAN | TINYINT(1) | INTEGER | NUMBER(1) |
decimal | NUMERIC | DECIMAL | REAL | NUMBER |
float | REAL | FLOAT | REAL | FLOAT |
timestamp | TIMESTAMP | DATETIME | TEXT | TIMESTAMP |
datetime | TIMESTAMP | DATETIME | TEXT | TIMESTAMP |
date | DATE | DATE | TEXT | DATE |
uuid | UUID | CHAR(36) | TEXT | CHAR(36) |
json | JSONB | JSON | TEXT | CLOB |
enum | CREATE TYPE … AS ENUM | ENUM(…) | TEXT CHECK (…) | VARCHAR2 CHECK (…) |
Relationships
Relationships use the pattern LEFT ||--o{ RIGHT : "label". The cardinality markers are:
| Meaning | Left | Right |
|---|---|---|
| Exactly one | || | || |
| Zero or one | |o | o| |
| One or more | }| | |{ |
| Zero or more | }o | o{ |
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.
erDiagram
USER {
int id PK
%% ::NN
string email
string bio
}CREATE TABLE "user" (
"id" SERIAL PRIMARY KEY,
"email" TEXT NOT NULL,
"bio" TEXT
);::DEFAULT(value) — Default value
erDiagram
USER {
int id PK
%% ::DEFAULT(true), ::NN
boolean active
%% ::DEFAULT(0)
int score
}"active" BOOLEAN NOT NULL DEFAULT true, "score" INTEGER DEFAULT 0
::CHECK(expr) — CHECK constraint
erDiagram
PRODUCT {
int id PK
%% ::CHECK(price > 0), ::NN
decimal price
%% ::CHECK(stock >= 0), ::DEFAULT(0)
int stock
}"price" NUMERIC NOT NULL CHECK (price > 0), "stock" INTEGER DEFAULT 0 CHECK (stock >= 0)
::ENUM(v1,v2,...) — Enum values
erDiagram
ORDER {
int id PK
%% ::ENUM(pending,shipped,delivered)
enum status
}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
erDiagram
ORDER {
int id PK
int user_id FK
timestamp created_at
%% ::INDEX([user_id, created_at])
%% ::INDEX([user_id], name: "idx_order_user")
}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.
erDiagram
%% ::RELATIONSHIP[onDelete: CASCADE]
USER ||--o{ ORDER : places
%% ::RELATIONSHIP[onDelete: SET NULL]
DEPT ||--o{ EMP : employsFOREIGN 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.
%% ::NN, ::DEFAULT(active)