Convert Mermaid ER to MySQL
Generate production-ready MySQL SQL code from your Mermaid ER diagrams in seconds.
How the Mermaid ER to MySQL conversion works
MySQL turns Mermaid input into InnoDB-ready DDL with a few dialect-specific habits: string maps to VARCHAR(255) rather than TEXT so columns stay indexable by default, enums are inline column types instead of separate objects, and every identifier is backtick-quoted β which is what lets a table named order (a reserved word) work 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
MySQL 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 MySQL 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` INT AUTO_INCREMENT PRIMARY KEY,
`email` VARCHAR(255) UNIQUE,
`name` VARCHAR(255) NOT NULL,
`created_at` DATETIME DEFAULT now()
);
CREATE TABLE `order` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`customer_id` INT,
`product_id` INT,
`total` DECIMAL DEFAULT 0
);
CREATE TABLE `product` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`sku` VARCHAR(255) UNIQUE,
`price` DECIMAL CHECK (price > 0),
`status` ENUM('draft', 'active', 'retired')
);
ALTER TABLE `order`
ADD CONSTRAINT `fk_order_customer_id`
FOREIGN KEY (`customer_id`) REFERENCES `customer`(`id`);
ALTER TABLE `order`
ADD CONSTRAINT `fk_order_product_id`
FOREIGN KEY (`product_id`) REFERENCES `product`(`id`); The same diagram that produced a CREATE TYPE on PostgreSQL emits an inline ENUM('draft', 'active', 'retired') column here β MySQL's native enum form. AUTO_INCREMENT replaces SERIAL, string became VARCHAR(255), and the foreign keys keep deterministic fk_order_customer_id names, which matters on MySQL where anonymous constraints make later ALTERs painful.
What the generator emits for MySQL
- CREATE TABLE statements with backtick-quoted identifiers, ready for InnoDB
- AUTO_INCREMENT primary keys for single integer PK columns
- inline ENUM(β¦) column types for ::ENUM(...) columns
- ALTER TABLE β¦ ADD CONSTRAINT foreign keys with ON DELETE / ON UPDATE actions
- inline COMMENT column options and COMMENT = table options
- CREATE INDEX / CREATE UNIQUE INDEX for composite and named indexes
Type mapping: Mermaid ER to MySQL
Diagram column types are logical β the generator maps each one to the idiomatic MySQL type. These are the exact mappings the engine uses:
| Diagram type | MySQL type | Notes |
|---|---|---|
int | INT | INT AUTO_INCREMENT when it is the auto-increment primary key |
string | VARCHAR(255) | use varchar(n) in the diagram to control length |
text | TEXT | longtext / mediumtext map to their native equivalents |
decimal | DECIMAL | decimal(10,2) passes precision through |
boolean | TINYINT(1) | the MySQL convention for booleans |
date | DATE | |
timestamp | DATETIME | |
uuid | CHAR(36) | stored as the canonical text form |
bigint | BIGINT | |
blob | BLOB |
Keys, relationships, and enums
A single `int id PK` column becomes `` `id` INT AUTO_INCREMENT PRIMARY KEY ``. Composite primary keys emit a table-level `PRIMARY KEY (a, b)` without auto-increment.
Relationships emit `ALTER TABLE β¦ ADD CONSTRAINT` statements with deterministic `fk_<table>_<column>` names β important on MySQL, where anonymous constraints make later migrations painful. ON DELETE / ON UPDATE actions come from the ::RELATIONSHIP directive.
MySQL supports enums natively on the column, so `::ENUM(active, inactive)` emits `status ENUM('active', 'inactive')` inline β no separate type object, no CHECK constraint.
Frequently asked questions
Why VARCHAR(255) instead of TEXT for string columns?
MySQL can't index a TEXT column without a prefix length, so the generator defaults string to VARCHAR(255). Write varchar(80) (or any length) as the column type in the diagram to control the size, or text to force TEXT.
How does a table named order survive in MySQL?
ORDER is a reserved word, but the generator backtick-quotes every identifier, so `order` is legal. You don't have to rename entities in the diagram to dodge SQL keywords.
Does the generated MySQL DDL use native ENUM columns?
Yes. ::ENUM(...) values are emitted as an inline ENUM(β¦) column type, which MySQL validates at the storage layer. PostgreSQL output uses CREATE TYPE instead; SQLite and Oracle fall back to CHECK constraints.
How are booleans represented?
As TINYINT(1), the MySQL convention. Most drivers and ORMs read TINYINT(1) back as a boolean automatically.