npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

orm2erd

v2.1.0

Published

Generate ERD diagrams (Mermaid, DBML, PlantUML, D2, and more) straight from your ORM's models/schema — no manual diagramming.

Readme

orm2erd

npm version CI node license codecov

You already built the app — your ORM models are the schema. orm2erd reads them and generates an ERD (Entity-Relationship Diagram) for you, instead of you drawing and maintaining one by hand.

Status: early development — see the tables below for what's supported today vs. planned.

orm2erd demo

Table of Contents

What it does

orm2erd scans your project, figures out which ORM you're using, and turns your existing models/schema into diagram code — Mermaid, DBML, PlantUML, D2, and more. No manual diagramming, no drift between your code and your docs.

detect ORM → resolve entry point(s) → parse/introspect → normalize to IR → emit diagram code(s) → write file(s)

Supported ORMs

| | ORM | Status | | --- | --- | --- | | | Prisma | ✅ Supported | | | Sequelize | ✅ Supported | | | Mongoose | ✅ Supported | | | TypeORM | ✅ Supported | | | Drizzle | ✅ Supported | | | MikroORM | ✅ Supported | | | BookShelf.js | 🚧 Planned | | | Waterline | 🚧 Planned | | | Objection.js | 🚧 Planned |

Output formats

| | Format | Status | | --- | --- | --- | | | Mermaid | ✅ Supported | | | DBML | ✅ Supported | | | PlantUML | ✅ Supported | | | D2 | ✅ Supported | | | nomnoml | ✅ Supported | | | QuickDBD | ✅ Supported | | | Graphviz DOT | ✅ Supported | | | Structurizr | ✅ Supported | | | Pikchr | ✅ Supported | | | erd (BurntSushi) | 🚧 Planned | | | draw.io | 🚧 Planned |

Requirements

Node.js >= 24.

Installation

Run without installing (recommended — always gets the latest version):

npx orm2erd
# or npx orm2erd@latest to bypass a locally cached version

pnpm dlx orm2erd
# or pnpm dlx orm2erd@latest

bunx orm2erd
# or bunx orm2erd@latest

Or install globally:

npm i -g orm2erd
orm2erd

Usage

Interactive:

npx orm2erd
┌  orm2erd
│
◇  Detected: prisma
◆  Entry point for prisma:
│  prisma/schema.prisma
◆  Output format(s):
│  mermaid
◆  Output path:
│  erd.mmd
◆  Type labels:
│  Canonical
◆  Entity/field names:
│  Table
◆  Relation edge labels:
│  Both
│
◇  Written to erd.mmd
│
└  Done

erd.mmd:

%% Generated by orm2erd.

erDiagram

  %% Entities
  User {
    int id PK "default: autoincrement()"
    string email UK
    string? name
  }

  Post {
    int id PK "default: autoincrement()"
    string title
    string? content
    boolean published "default: false"
    int authorId FK
  }

  Comment {
    int id PK "default: autoincrement()"
    string text
    int postId FK
    int authorId FK
  }

  Tag {
    int id PK "default: autoincrement()"
    string name UK
  }

  %% Relationships
  User ||--o{ Post : "posts (authorId)"
  User ||--o{ Comment : "comments (authorId)"
  Post ||--o{ Comment : "comments (postId)"
  Post }o--o{ Tag : "tags"

Non-interactive (CI-friendly):

npx orm2erd --orm prisma --entry ./prisma/schema.prisma --format mermaid,dbml --out ./erd

You can select multiple output formats in a single run — the schema is parsed once and reused across every format you pick. --out accepts either a bare name (erd, gets each format's extension appended) or a full filename (erd.md, used exactly as given when there's only one output format).

By default, field types are emitted in a canonical, portable form (e.g. string, int). Pass --type-mode native to emit the ORM's own type names instead (e.g. Prisma's String, Int):

npx orm2erd --orm prisma --entry ./prisma/schema.prisma --format mermaid --type-mode native

When your ORM maps model/field names to different physical table/column names (e.g. Prisma's @@map/@map, Sequelize's tableName/field), --names controls which names show up in the diagram. It defaults to table — an ERD describes a database, so it matches what you'd see in psql — with model (the ORM's own names) and both (physical name plus the ORM name as an alias, where the output format supports one) also available:

model User {
  id       Int    @id @default(autoincrement())
  fullName String @map("full_name")

  @@map("users")
}
npx orm2erd --orm prisma --entry ./prisma/schema.prisma --format mermaid --names both
%% Generated by orm2erd.

erDiagram

  %% Entities
  users["User"] {
    int id PK "default: autoincrement()"
    string full_name "alias: fullName"
  }

  %% Relationships

Relation edge labels have a similar knob, --relation-label <both|alias|column>both (the default) shows the association alias and appends the FK column only when it disambiguates two relations between the same entity pair (e.g. posts (authorId)); alias/column pin the label to just one.

If your source names don't already use the letter-casing you want in the diagram, --case <mode> rewrites every rendered identifier (entity/field names, relation labels, enum type names) into snake, screaming_snake, camel, pascal, kebab, title, lower, or upper — regardless of whether the source is snake_case, camelCase, or PascalCase to begin with. It never touches type labels, enum member values, or the secondary alias shown by --names both, since those are either fixed vocabulary or meant to show the real ORM identifier unmodified:

npx orm2erd --orm prisma --entry ./prisma/schema.prisma --format mermaid --case screaming_snake

If your ORM/DB convention doesn't already give entity names the plural/singular form you want, --inflect <plural|singular> forces one — entity/table names only, never field names or the --names both alias, since pluralizing a field like email or createdAt doesn't make sense the way pluralizing a whole entity does. It runs before --case, so --inflect plural --case kebab on an entity PostTag produces post-tags, not post-tag-s:

npx orm2erd --orm prisma --entry ./prisma/schema.prisma --format mermaid --inflect plural

Keeping the ERD in sync (CI)

Commit your generated ERD, then use --check to fail CI whenever the committed file no longer matches what your current models would produce — so the diagram can never silently drift out of date:

npx orm2erd --orm prisma --entry ./prisma/schema.prisma --format mermaid --out ./docs/erd.mmd --check
  • matches → prints ERD up to date and exits 0
  • differs → prints a diff of what changed and exits 1
  • missing → tells you to generate it first and exits 1

--check never touches the filesystem, so it's safe in a pre-commit hook or a pull-request check. In CI it's flag-driven and non-interactive (same detection rules as every other run — no TTY, or CI env var set): pass it the same output-affecting flags you generated with (--format, --out, and --type-mode/--names/--relation-label/--case/--inflect if you use them), or it will report drift against a differently-rendered file.

Run locally from a terminal, --check prompts like any other run if something's ambiguous (which ORM, which entry point, ...) — including --out itself if you don't pass it. That prompt is deliberately different from a plain write's: there's no pre-filled guess to accept by hitting Enter, since a wrong guess there means silently checking the wrong file. You have to type the actual path of the file that's already committed.

Every generating (non---check) run also writes a small <out>.orm2erd-model.json cache file alongside the diagram — a snapshot of the underlying model, gitignored by default. Add --summary to --check to use it: instead of a raw line diff, you get a structural, schema-level summary grouped by entity:

npx orm2erd --orm prisma --entry ./prisma/schema.prisma --format mermaid --out ./docs/erd.mmd --check --summary
users: +column "last_login_at"
posts → tags: cardinality changed (1-n → n-n)

Since the snapshot is a local cache (not committed), a CI runner or fresh clone with no prior local run won't have one yet — --summary falls back to the raw line diff with a note in that case, which is expected, not an error.

Drop it into a workflow:

# .github/workflows/erd.yml
name: ERD in sync
on: [pull_request]
jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 24
      - run: npm ci
      - run: npx orm2erd --orm sequelize --entry ./models/index.js --format mermaid --out ./docs/erd.mmd --check

Note: ORMs parsed statically from a schema file (e.g. Prisma) need nothing but that file in CI. ORMs introspected by importing your models require CI to be able to run them (install dependencies, and provide any env vars the models need at import time) — same as generating the ERD normally. See docs/adapters.md for how each ORM is parsed.

Same flow works for any supported ORM — just swap --orm and --entry. For example, a Sequelize project with associations declared via hasMany/belongsTo:

npx orm2erd --orm sequelize --entry ./models/index.js --format mermaid --out ./erd

Sequelize pluralizes a model's table name by default (Userusers) unless tableName is set explicitly — with --names defaulting to table, that's what shows up in the diagram:

erd.mmd:

%% Generated by orm2erd.

erDiagram

  %% Entities
  Users {
    int id PK
    string email UK
    string? name
    datetime createdAt
    datetime updatedAt
  }

  Posts {
    int id PK
    string title
    boolean? published "default: false"
    datetime createdAt
    datetime updatedAt
    int? authorId FK
  }

  %% Relationships
  Users ||--o{ Posts : "posts (authorId)"

Flags

| Flag | Description | | --- | --- | | --orm <name> | ORM to use — see Supported ORMs. Skips detection. | | --entry <path> | Path to the ORM's schema/model entry. Skips the entry-point prompt. | | --format <formats> | Output format(s), comma-separated, or all for every supported format — see Output formats. | | --out <path> | Output path — bare name gets each format's extension appended; a full filename is used as-is when there's only one format. A directory (trailing slash, or an existing directory) writes erd.<ext> inside it. | | --type-mode <mode> | Type labels to emit: canonical (portable, default) or native (ORM-specific). | | --names <mode> | Entity/field identifiers to emit: table (physical table/column names, default), model (ORM model/field names), or both (physical name, with the ORM name as an alias where the format supports one). | | --relation-label <mode> | Relation edge label: both (association alias, plus the FK column when it disambiguates two relations between the same entity pair — default), alias, or column. | | --case <mode> | Letter-casing for rendered identifiers: preserve (source casing as-is, default), snake, screaming_snake, camel, pascal, kebab, title, lower, or upper. | | --inflect <mode> | Pluralization for entity/table identifiers only: preserve (source number as-is, default), plural, or singular. | | --check | Verify the committed ERD file(s) are up to date instead of writing. Exits non-zero on drift or if a file is missing; writes nothing. See Keeping the ERD in sync. | | --summary | With --check, print a structural (schema-level) diff grouped by entity instead of the raw line diff, using the gitignored <out>.orm2erd-model.json cache written on the last generating run. Falls back to the raw diff when no cache is available yet or the drift is formatting-only. Requires --check. | | --stdout | Print the diagram to stdout instead of writing a file — requires exactly one --format. Status output goes to stderr, so the diagram can be piped cleanly (e.g. orm2erd ... --stdout > erd.mmd or into another tool). | | --copy | Copy the diagram to the clipboard instead of writing a file — requires exactly one --format. | | --verbose | Show log output from the target codebase during extraction (suppressed by default). | | -y, --yes | Skip interactive prompts; use the default for any flag not explicitly passed — the same resolution CI/non-interactive mode already uses. Useful once you know your flags and don't want to re-answer the picker every run. | | -v, --version | Output the current version. | | -h, --help | Show usage and examples. |

In a TTY, any flag you omit falls back to an interactive prompt. In CI (no TTY, or CI=true), or when -y/--yes is passed, prompts are skipped — pass --orm, --entry, and --format explicitly, or the run exits with an error telling you which one is missing.

For Prisma, if a prisma.config.* file is present, its schema field is respected as the entry point's default candidate, same as the Prisma CLI.

Why

Diagrams drawn by hand go stale the moment the schema changes. Your ORM already has an accurate, structured picture of your data model — orm2erd just reads that instead of asking you to redraw it.

Contributing

See CLAUDE.md for architecture, the adapter/emitter contract, and design decisions. For exactly how each ORM is detected and parsed, see docs/adapters.md. To report a security vulnerability, see SECURITY.md.

License

MIT