ErdDocs

Formats

ErdDocs reads ten formats and exports to eight. Each entry below says what we make of the format and, where there is one, the limit. The limits are the part that saves you an afternoon.

What you can paste

The format is detected from the text. In the tool, the selector above the schema panel shows what was decided and lets you disagree.

  • A dump from pg_dump --schema-only or mysqldump --no-data, a migration file, or any script of CREATE TABLE statements.

    Four dialects are read: PostgreSQL, MySQL, SQL Server (T-SQL from SSMS, GO batches included) and SQLite (.schema output, bracketed names included). For PostgreSQL and MySQL the comments are read too, and they become the description column of the data dictionary. Partitions are folded back into the parent table. A real dump can carry fifty-five of them for one table. Relations the dump never declared are recovered from column names (user_id → users) and drawn dashed. Rails, Django and WordPress schemas need this most, because they often declare almost no foreign keys at all.

  • schema.prisma, exactly as it sits in your repository.

    We draw the database Prisma would create, not the models. @@map and @map names win. A relation shows on the side that holds the scalar foreign key. An implicit many-to-many becomes the join table Prisma generates for it. The cal.com schema, with 105 models, reads with no errors.

  • The dbdiagram.io language: Table blocks and Ref lines.

    Tables, columns, settings, enums and every form of Ref, including the inline one. Written by hand or exported from another tool, it makes no difference.

  • An erDiagram block, the kind that lives in a README.

    Both ends of a Mermaid relation carry their own glyphs, and the left and right ends use different sets. We read the pair rather than guessing from one side. Attribute blocks with PK and FK markers come through as keys.

  • PlantUML

    An @startuml file with entity blocks.

    Entities, their attributes, primary and foreign key stereotypes, and the relation glyphs between them. PlantUML is also an export, so a diagram can go out the way it came in.

  • The models.py of an app, pasted as it is. No manage.py, no settings, no installation.

    We draw the tables Django creates. A ForeignKey named author becomes the column author_id, a model without an explicit key gets id, and a ManyToManyField becomes a table of its own. Django executes on_delete and default in Python, and they never reach the database, so they are not drawn. Only db_default is. There is one gap, and the tool states it once per file. Django prefixes table names with the app label, and a pasted file cannot say which app it came from. Meta.db_table is used wherever it appears.

  • Declarative models: Column, ForeignKey, __tablename__.

    Column types map to their SQL equivalents, ForeignKey becomes a relation, and a composite primary key stays composite. relationship() adds nothing that the columns do not already say. So it is read, but it never invents an edge.

  • sequelize.define or Model.init, together with the associations below them.

    field: overrides win over the attribute name, and tableName over the model name. Some columns are declared only by an association, as in Order.belongsTo(Customer, { foreignKey: "customer_id" }). Such a column is synthesized from the target key’s type, so the relation does not point at a column missing from the card. A name that cannot be confirmed is never invented.

  • @Entity classes with their decorators.

    The naming rules come from TypeORM’s own DefaultNamingStrategy. The table is snake_case of the class, and the join table is snake_case(first_prop_second). But columns stay camelCase (authorId). That asymmetry is why half the SnakeNamingStrategy packages on npm exist. Ownership decides existence, as in TypeORM itself. @OneToMany alone produces nothing. @OneToOne gives a column only where @JoinColumn sits, and @ManyToMany gives a table only where @JoinTable sits.

  • Laravel

    The contents of database/migrations, pasted one after another.

    Migrations are history rather than a declaration, so they are replayed in order. A create call makes a table. A later table() call adds a column, drops another, renames a third. The result is the sum. down() is deliberately ignored. Every file contains the instructions to undo itself, so a folder read literally would parse into almost nothing. foreignId()->constrained() is resolved by Laravel’s own guess (user_id → users). But the relation is only drawn when that table is in the text you pasted.

How well it reads real schemas

ErdDocs is measured on 89 public schemas taken from other people’s repositories. They are production dumps, ORM models and diagram files as they were committed, not samples written for the test. The table shows the share of tables and of declared relations that came out of the parser, as of 2026-10-16.

FormatSchemasTables readRelations read
SQL2399% 2,587 of 2,60796% 2,242 of 2,341
Prisma23100% 553 of 55397% 749 of 776
SQLAlchemy2998% 276 of 28297% 173 of 179
DBML1495% 90 of 9598% 379 of 388

SQL counts PostgreSQL, MySQL, SQL Server and SQLite dumps. Relations are the ones the schema declares. Links recovered from column names come on top and are drawn dashed.

What you can take away

Every export is free, and the images carry a small ErdDocs mark. The $9 license removes it. The free limit applies to how many tables get drawn, not to what gets converted. A text export always carries the whole schema.

  • PNG image

    The canvas as you arranged it, at the resolution a document needs.

  • SVG image

    Vector, so it survives zooming in a wiki or a PDF. This is also what the README embed points at.

  • An erDiagram block to commit next to the code; GitHub renders it natively.

  • Opens in dbdiagram.io, and is the most readable way to keep a schema under review.

  • PlantUML

    example →

    For teams whose documentation pipeline already speaks it.

  • SQL (PostgreSQL or MySQL)

    example →

    It is for inputs that are not SQL, since SQL to SQL is a no-op. Prisma, DBML or ORM models become working DDL. Relations we recovered from column names are printed commented out. Otherwise the script would pass on an empty database and fail on a full one.

  • Data dictionary

    example →

    The document, not the picture. It covers every table, column, type, nullability, default, index and relation, and you print it to PDF from the browser. This is the part the license is for.

  • README embed, iframe embed, project file

    example →

    The README embed is a markdown snippet plus the SVG it points at. The iframe is for a page you own. The project file is an .erddocs.json: a share link written to disk, for schemas too big for a URL.

Questions before you paste

How does it know which format I pasted?

ErdDocs looks at the text. A schema.prisma has model blocks, a Mermaid file opens with erDiagram, and Laravel migrations are PHP with Schema::create in them. These sniffs are specific enough that the diagram languages are checked before SQL. SQL is the fallback rather than a positive match. In the tool, the selector above the schema panel shows what was decided, and you can overrule it. Overruling it helps most with an ambiguous file, such as a Prisma schema quoted inside a JavaScript file.

Does the format change what the data dictionary contains?

Only in as much as the source carries the facts. A SQL dump knows about indexes and comments, so those columns fill in. A Mermaid diagram knows neither, so they stay empty rather than being invented. Everything a format does state reaches the document the same way, whichever format it came from: tables, columns, types, nullability, defaults, keys and relations.

My schema has no foreign keys at all. Is it worth pasting?

Yes, and this is common. Rails, Django and Laravel projects often declare relations in the application rather than in the database, and a WordPress dump has none whatsoever. Relations are recovered from column names (user_id points at users). They are drawn as dashed lines, so the guess is never mistaken for something your schema actually declares. One switch hides them everywhere, including the dictionary and every export.

Is anything uploaded when I paste?

No. Every parser on this page runs in your browser, and the schema never leaves the tab. No request carries it, and you can check that in your own network panel. That is also why there is no account. There is nothing on our side to attach one to.

Nothing to paste yet? The example schemas are fourteen real schemas with the SQL to copy. Or start from your own dump on the home page. Whatever the format, it is read in the browser and never uploaded.

Not sure what the picture is called? ER diagram explains the notation from entities up. Schema diagram covers the narrower term for the tables a database actually holds, and the difference between the two.