mermaid-er-diagrams

Create entity-relationship diagrams with Mermaid for database schema design and data modeling

Mermaid Entity-Relationship Diagrams

Scope: Database schema and data relationship visualization with Mermaid.js Lines: ~400 Last Updated: 2025-10-27 Format Version: 1.0 (Atomic)


When to Use This Skill

Activate this skill when:


Core Concepts

Concept 1: Basic Entity and Relationship Syntax

Simple entity definition:

erDiagram
    CUSTOMER
    ORDER
    PRODUCT

With relationships:

erDiagram
    CUSTOMER ||--o{ ORDER : places
    ORDER ||--|{ LINE_ITEM : contains
    PRODUCT ||--o{ LINE_ITEM : includes

Relationship anatomy:

ENTITY_A |cardinality|--relationship--|cardinality| ENTITY_B : label
         └─────┬─────┘                └─────┬─────┘
               └─ Left side                  └─ Right side

Concept 2: Cardinality Notation

Zero or One |o or o|:

erDiagram
    USER ||--o| PROFILE : has

Each user has zero or one profile

Exactly One || or ||:

erDiagram
    ORDER ||--|| INVOICE : generates

Each order has exactly one invoice

Zero or More }o or o{:

erDiagram
    AUTHOR }o--o{ BOOK : writes

Authors can write zero or more books

One or More }| or |{:

erDiagram
    COMPANY ||--|{ EMPLOYEE : employs

A company has one or more employees

Visual reference:

erDiagram
    A ||--|| B : "one-to-one (exactly)"
    C ||--o| D : "one-to-zero-or-one"
    E ||--|{ F : "one-to-one-or-more"
    G ||--o{ H : "one-to-zero-or-more"
    I }o--o{ J : "zero-or-more to zero-or-more"

Concept 3: Relationship Types

Identifying relationship (solid line --):

erDiagram
    ORDER ||--|{ LINE_ITEM : contains

    ORDER {
        int order_id PK
        date order_date
    }

    LINE_ITEM {
        int order_id PK,FK
        int line_number PK
        int quantity
    }

LINE_ITEM cannot exist without ORDER (foreign key is part of primary key)

Non-identifying relationship (dashed line ..):

erDiagram
    CUSTOMER ||..o{ ORDER : places

    CUSTOMER {
        int customer_id PK
        string name
    }

    ORDER {
        int order_id PK
        int customer_id FK
        date order_date
    }

ORDER can exist independently, just references CUSTOMER

Concept 4: Attributes and Keys

Attribute definition:

erDiagram
    USER {
        int id PK
        string email UK
        string username UK
        string password_hash
        datetime created_at
        datetime updated_at
    }

Key types:

erDiagram
    PRODUCT {
        uuid product_id PK "Primary Key"
        string sku UK "Unique identifier"
        int category_id FK "References category"
        string name
        decimal price
        int stock_count
    }

    CATEGORY {
        int category_id PK
        string name UK
        string description
    }

    PRODUCT }o--|| CATEGORY : belongs_to

Key annotations:

Data types (common conventions):

Concept 5: Complex Relationships

Self-referential:

erDiagram
    EMPLOYEE ||--o{ EMPLOYEE : manages

    EMPLOYEE {
        int employee_id PK
        string name
        int manager_id FK
        string position
    }

Many-to-Many with junction table:

erDiagram
    STUDENT ||--o{ ENROLLMENT : ""
    ENROLLMENT }o--|| COURSE : ""

    STUDENT {
        int student_id PK
        string name
        string email UK
    }

    ENROLLMENT {
        int enrollment_id PK
        int student_id FK
        int course_id FK
        date enrolled_date
        string grade
    }

    COURSE {
        int course_id PK
        string code UK
        string title
        int credits
    }

Common Patterns

Pattern 1: E-commerce Schema

erDiagram
    CUSTOMER ||--o{ ORDER : places
    ORDER ||--|{ ORDER_ITEM : contains
    PRODUCT ||--o{ ORDER_ITEM : included_in
    PRODUCT }o--|| CATEGORY : belongs_to
    ORDER ||--o| PAYMENT : has

    CUSTOMER {
        uuid customer_id PK
        string email UK
        string name
        string phone
        datetime created_at
    }

    ORDER {
        uuid order_id PK
        uuid customer_id FK
        datetime order_date
        decimal total_amount
        string status
    }

    ORDER_ITEM {
        uuid order_id PK,FK
        int line_number PK
        uuid product_id FK
        int quantity
        decimal price
        decimal subtotal
    }

    PRODUCT {
        uuid product_id PK
        string sku UK
        string name
        decimal price
        int stock
        uuid category_id FK
    }

    CATEGORY {
        uuid category_id PK
        string name UK
        string description
    }

    PAYMENT {
        uuid payment_id PK
        uuid order_id FK
        decimal amount
        string method
        datetime processed_at
        string status
    }

Pattern 2: User Authentication System

erDiagram
    USER ||--o{ SESSION : has
    USER ||--o{ ROLE_ASSIGNMENT : has
    ROLE ||--o{ ROLE_ASSIGNMENT : assigned_to
    ROLE ||--o{ PERMISSION : includes
    USER ||--o| USER_PROFILE : has

    USER {
        uuid user_id PK
        string email UK
        string password_hash
        boolean email_verified
        datetime created_at
    }

    SESSION {
        uuid session_id PK
        uuid user_id FK
        string token UK
        datetime expires_at
        string ip_address
    }

    ROLE {
        int role_id PK
        string name UK
        string description
    }

    ROLE_ASSIGNMENT {
        uuid user_id PK,FK
        int role_id PK,FK
        datetime assigned_at
    }

    PERMISSION {
        int permission_id PK
        int role_id FK
        string resource
        string action
    }

    USER_PROFILE {
        uuid user_id PK,FK
        string display_name
        string bio
        string avatar_url
        json preferences
    }

Pattern 3: Blog/CMS Schema

erDiagram
    USER ||--o{ POST : authors
    POST ||--o{ COMMENT : has
    USER ||--o{ COMMENT : writes
    POST }o--o{ TAG : tagged_with
    POST_TAG }o--|| POST : ""
    POST_TAG }o--|| TAG : ""
    POST }o--|| CATEGORY : belongs_to

    USER {
        uuid user_id PK
        string username UK
        string email UK
        string password_hash
        datetime created_at
    }

    POST {
        uuid post_id PK
        uuid author_id FK
        uuid category_id FK
        string title
        string slug UK
        text content
        string status
        datetime published_at
    }

    COMMENT {
        uuid comment_id PK
        uuid post_id FK
        uuid user_id FK
        uuid parent_comment_id FK
        text content
        datetime created_at
    }

    TAG {
        int tag_id PK
        string name UK
        string slug UK
    }

    POST_TAG {
        uuid post_id PK,FK
        int tag_id PK,FK
    }

    CATEGORY {
        uuid category_id PK
        string name UK
        string slug UK
        text description
    }

Pattern 4: Multi-Tenancy SaaS

erDiagram
    ORGANIZATION ||--|{ USER : employs
    ORGANIZATION ||--|{ PROJECT : owns
    PROJECT ||--o{ TASK : contains
    USER ||--o{ TASK : assigned_to
    USER ||--o{ ACTIVITY_LOG : generates
    ORGANIZATION ||--o| SUBSCRIPTION : has

    ORGANIZATION {
        uuid org_id PK
        string name UK
        string slug UK
        datetime created_at
    }

    USER {
        uuid user_id PK
        uuid org_id FK
        string email UK
        string name
        string role
    }

    PROJECT {
        uuid project_id PK
        uuid org_id FK
        string name
        text description
        string status
    }

    TASK {
        uuid task_id PK
        uuid project_id FK
        uuid assigned_to FK
        string title
        text description
        string status
        date due_date
    }

    SUBSCRIPTION {
        uuid subscription_id PK
        uuid org_id FK
        string plan_name
        decimal price
        datetime expires_at
    }

    ACTIVITY_LOG {
        uuid log_id PK
        uuid user_id FK
        uuid org_id FK
        string action
        json metadata
        datetime created_at
    }

Best Practices

1. Clear Entity Naming

erDiagram
    %% Good: Singular nouns, uppercase
    USER ||--o{ ORDER : places
    ORDER ||--|{ LINE_ITEM : contains

    %% Bad: Plural, lowercase, inconsistent
    users ||--o{ order : places
    order ||--|{ order_line_items : contains

2. Explicit Foreign Keys

erDiagram
    POST {
        uuid post_id PK
        uuid author_id FK "References USER"
        uuid category_id FK "References CATEGORY"
    }

    USER {
        uuid user_id PK
    }

    CATEGORY {
        uuid category_id PK
    }

    POST }o--|| USER : authored_by
    POST }o--|| CATEGORY : in_category

3. Normalize Appropriately

Good: Many-to-many with junction table

erDiagram
    STUDENT ||--o{ ENROLLMENT : ""
    COURSE ||--o{ ENROLLMENT : ""

    ENROLLMENT {
        int student_id PK,FK
        int course_id PK,FK
        date enrolled_date
        string grade
    }

Bad: Denormalized array (avoid in SQL)

erDiagram
    STUDENT {
        int student_id PK
        string name
        json course_ids
    }

4. Use Descriptive Relationship Labels

erDiagram
    %% Good: Clear action verbs
    USER ||--o{ POST : authors
    COMPANY ||--|{ EMPLOYEE : employs
    ORDER ||--o| SHIPMENT : fulfilled_by

    %% Bad: Generic or missing
    USER ||--o{ POST : has
    COMPANY ||--|{ EMPLOYEE : related
    ORDER ||--o| SHIPMENT : ""

Anti-Patterns

❌ Missing Cardinality

erDiagram
    USER -- ORDER : places

Problem: Relationship type unclear

✅ Better:

erDiagram
    USER ||--o{ ORDER : places

❌ No Primary Keys

erDiagram
    USER {
        string email
        string name
    }

✅ Better:

erDiagram
    USER {
        uuid user_id PK
        string email UK
        string name
    }

❌ Overloaded Entity

erDiagram
    ENTITY {
        int id PK
        string type
        json data1
        json data2
        json data3
        json data4
    }

Problem: God entity, lacks structure

✅ Better: Split into separate entities with clear responsibilities

❌ Circular Dependencies Without Nullables

erDiagram
    USER ||--|| PROFILE : has
    PROFILE ||--|| USER : belongs_to

Problem: Cannot create either without the other

✅ Better:

erDiagram
    USER ||--o| PROFILE : has

Integration Tips

Related Skills

Resources