Under the hood

How SchemaDrawer works

From a multipart upload to an SVG: what happens to your files, what runs where, what the diagram contains and what it leaves out.

The pipeline

  1. Upload. POST /api/v1/drawing takes your migration files as multipart form data, together with the tool, the engine and its version. The files are stored, the job is recorded as PENDING, queued, and the call returns 202 with the job id at once. A drawing is never done twice for the same Request-Id: a retried request gets the first job back.
  2. A fresh database starts. A worker takes the job and starts an empty container of the engine and version you asked for, on a private Docker network created for this drawing. The database is created for the drawing and destroyed with it.
  3. The real tool runs your files. Each migration tool has its own container image, either the vendor's official one (Flyway, Liquibase, golang-migrate, dbmate, Atlas) or one built from a pinned Dockerfile (Django, Prisma, EF Core, Rails and the rest). The container joins the database's network, receives a copy of your files, and the tool's own command runs: flyway migrate, prisma migrate deploy, python -m django migrate, dotnet build followed by the EF Core migrator, and so on. Credentials travel in environment variables, never on the command line.
  4. The schema is read back. Over JDBC, from the engine's own metadata: every table, its columns with the type names the engine reports, nullability, primary keys and foreign keys. The tool's history table is removed, system schemas are skipped.
  5. The diagram is laid out and rendered. Tables become nodes, foreign keys become edges labelled with the referencing column, and the Eclipse Layout Kernel arranges them left to right with orthogonal routing and a fixed random seed, so the same schema always produces the same picture. The result is plain SVG, stored next to your files; the job turns SUCCESS with a link that is valid for an hour.

If anything fails along the way, the job ends as ERROR with the migration tool's own output, exit code included. A broken CREATE TABLE comes back with the same message Flyway or Prisma would print on your machine.

What is drawn, and what is not

DrawnNot drawn
Every table the migrations leave behind, in every user schema of the engineViews and materialized views, sequences, synonyms
Columns with the type name the engine reports (int4, varchar, timestamptz on PostgreSQL; INT, VARCHAR on MySQL; NUMBER, VARCHAR2 on Oracle)Lengths, precision and scale, defaults, auto-increment, comments
Primary keys, including composite ones, marked PK and set in boldUnique and check constraints, indexes, triggers
Foreign keys, one arrow per referencing column, labelled with the column name, marked FK on the columnConstraint names, ON DELETE and ON UPDATE rules
Nullability is read and kept in the modelIt is not marked in the picture by default

Bookkeeping tables left out

Every migration tool records what it has applied in a table of its own. Only the chosen tool's tables are removed, by name, so a table of yours with one of these names would disappear as well:

ToolHidden tables
Flywayflyway_schema_history
Liquibasedatabasechangelog, databasechangeloglock
golang-migrate, dbmate, ActiveRecordschema_migrations (ActiveRecord also ar_internal_metadata)
Alembicalembic_version
goosegoose_db_version
Atlasatlas_schema_revisions
Prisma_prisma_migrations
DbUpschemaversions
FluentMigratorVersionInfo
Doctrinedoctrine_migration_versions
Laravel, TypeORMmigrations
Djangodjango_migrations
Drizzle__drizzle_migrations
Sqitchchanges, dependencies, events, projects, releases, tags
Diesel__diesel_schema_migrations
EF Core__EFMigrationsHistory
MikroORMmikro_orm_migrations
Knexknex_migrations, knex_migrations_lock
SequelizeSequelizeMeta

Engines and versions

PostgreSQL (10, 11, 12, 13, 14, 15, 16, 17, 18), MySQL (8.0, 8.4, 9), MariaDB (10.6, 11.4, 11.8, 12), Microsoft SQL Server (2017, 2019, 2022, 2025), Oracle Database (21, 23), SQLite (latest). Each version is a stock image started fresh for every drawing. SQLite is a library rather than a server, so its one version is called latest and every tool brings its own copy.

Limits and timing

  • Uploads of up to 100 MB and 2000 files per drawing. Larger uploads are answered with 413, malformed ones with 400 and a message saying what is wrong.
  • A drawing may take up to 120 seconds, from starting the database to reading its schema. Past that, its containers are killed and the job ends as ERROR with a message saying the drawing did not finish in time.
  • A job that waits in the queue for more than 10 minutes is not drawn; it ends as ERROR with the message timeout.
  • In practice a drawing takes a few seconds when the images are warm: in our runs the PetClinic sample took 6.4 seconds on PostgreSQL 18, 10.4 seconds on MySQL 9 and 16 seconds on SQL Server 2025, most of it waiting for the database to accept connections.
  • The diagram link is valid for one hour. Download the SVG and keep it in your repository.

Your files

The uploaded files are stored on AWS in Ireland (eu-west-1) so that a worker can run them, and the throw-away database is destroyed as soon as the drawing ends. SchemaDrawer never connects to a database of yours, and it needs no credentials from you. Enterprise plans will add retention controls, data residency choices and self-hosting; until then, upload only migrations you are comfortable sending to a third-party service.

Why not parse the SQL?

Because a migration is only defined by what the tool and the engine do with it. Half of the supported tools do not even take SQL as input: Django, EF Core, Rails, Alembic, Knex, Laravel and FluentMigrator migrations are code that generates DDL for a specific engine at run time. The other half write SQL in six dialects with vendor types, procedural blocks and conditional statements. A parser is always a subset of the engine; the engine is the truth. More on that in the blog.

Built with

  • Docker containers per drawing, one for the engine and one for the tool
  • The vendors' official tool images where they exist, pinned Dockerfiles elsewhere
  • JDBC metadata for the schema, the same source your IDE uses
  • Eclipse Layout Kernel (ELK) for the layout, plain SVG for the output
  • A queue between the API and the workers, so a burst of uploads waits rather than fails
The PetClinic schema drawn on MySQL 9: the same tables as on PostgreSQL, with INT, VARCHAR and DATE as the type names

The same migrations on MySQL 9: same tables, MySQL's type names.

From the blog

Further reading

How to Generate an ER Diagram from Migration Files

Three ways to turn Flyway, Liquibase, Prisma or Django migrations into an ER diagram, and a step-by-step guide to the one that always matches reality.

Draw your schema in the next five minutes

One HTTP call with your migration files, one poll, one SVG. Free while we build it.