GO திசைGoDisai

Story 7 · drawDB

The Whiteboard That Lives in Your Browser

“A schema drawn on a photo of a whiteboard is out of date the moment someone adds a column.”

18 min read · by Syed Jafer K

1. The Diagram That Was a Photo

Ayisha ran a small software studio in Coimbatore with four people and one big client. The client was a group of eye clinics spread across Tiruppur, Erode and Pollachi, and the studio was building their appointment and prescription system.

Iqra was the studio's database person. She had joined six months earlier from a bank, where nobody was allowed to add a column without a diagram, a review and a signature. At the studio, the schema lived in three places, and none of them agreed.

The first was a whiteboard in the meeting room. Iqra had drawn the tables there in the first week, and someone had taken a photo and posted it in the team's WhatsApp group. That photo was still pinned. It showed eleven tables. The real database now had nineteen.

The second was an online diagram tool that Ayisha had signed up for a year ago. The free plan allowed three diagrams. The studio had used all three, so new diagrams were being drawn over old ones.

The third was the migrations folder, which was the only one that was always right, and the one that nobody could read at a glance.

Two things happened in the same week of October.

The clinic group's IT head sent a vendor questionnaire. One question asked for a list of every third-party service that stored any part of the system's design or data, with the country it was hosted in. Ayisha realised that the full patient schema, with column names like aadhaar_last4, diabetic and iop_left_eye, was sitting on someone else's server in another country, under her personal account.

Then the diagram tool sent an email. The free plan was being reduced to one diagram.

Iqra said what she had been thinking for months. "We need a diagram tool we control. It should read our SQL, write our SQL, and not need an account."

Ayisha had seen a link go around on a developer forum earlier that year. She searched her bookmarks and found it. drawDB.


2. What drawDB Is

drawDB is a free, open-source editor for database diagrams. You open it in a browser, draw tables and the relationships between them, and export the result as SQL for the database you use. It can also do the reverse. You paste in the CREATE TABLE statements of an existing database and it draws the diagram for you.

It was started in 2024 by an independent developer who goes by 1ilit on GitHub, and it is developed in the open at drawdb-io/drawdb with help from many contributors. It became popular very quickly, mostly because of one design choice.

There is no backend for the editor. drawDB is a React application built with Vite. When you build it, you get a folder of HTML, JavaScript and CSS. Any web server that can serve static files can host it. There is no database to run, no user accounts and no sign-up.

The source is licensed under the AGPL-3.0. That matters later in this chapter.

Iqra liked it for a reason she said out loud. "The tool runs in my browser. The only server involved is the one that sends me the files. If we host those files ourselves, the schema never leaves the laptop."


3. What drawDB Does

Part What it gives you
Tables Fields with type, primary key, unique, not null, auto increment, default, check and comment
Relationships Drag from one field to another, then set the cardinality and the on update and on delete actions
Databases MySQL, PostgreSQL, SQLite, MariaDB, SQL Server, or a generic diagram
Types and enums Custom types and enums for databases that support them
Subject areas Coloured boxes that group related tables, such as billing or clinical
Notes Sticky notes on the canvas for decisions and open questions
Import SQL CREATE TABLE statements, or a diagram file exported earlier
Export SQL for your database, images (PNG, JPEG, SVG), PDF and JSON
Issues panel Warns about problems like duplicate names or a relationship between mismatched types
Templates Ready-made example schemas to learn from

Ayisha looked at the export row the longest. "So the diagram and the migration can come from the same drawing."

"The first draft of the migration," Iqra said. "We still read it before we run it."


4. Where Your Diagrams Actually Live

Before installing anything, Iqra wanted the team to understand one thing, because it changes how you use the tool.

drawDB saves your diagrams inside the browser, in a small database every browser has called IndexedDB. There is no server copy.

That has three consequences.

  1. Your diagrams are private by default. Nothing is uploaded when you draw. The self-hosted server only ever sends the application files.
  2. Your diagrams are tied to one browser on one machine. A diagram drawn in Firefox on Iqra's laptop is not visible in Chrome on the same laptop, and not visible at all on Ayisha's laptop.
  3. Clearing site data deletes your diagrams. So does a private window when you close it, and so does a browser profile that gets reset.

"So the browser is the whiteboard," Ayisha said. "And the eraser is in the settings menu."

"Which is why the real copy goes into git," Iqra said. "I will show you."


5. Before You Start

Iqra wrote the checklist in the studio's wiki.

  1. A Linux server with Docker, or any machine with Node 20 or newer if you would rather build the static files yourself. The studio's small VPS already ran Caddy for two client staging sites, so drawDB went there.
  2. A name for it. They chose schema.studio.example.
  3. Caddy in front for HTTPS, as in chapter 3.
  4. A decision about who may open it. The studio did not want the editor public, even though it stores nothing on the server.

6. Building It With Docker

The repository includes a Dockerfile. It builds the application with Node and then copies the finished files into a small Nginx image that serves them.

git clone https://github.com/drawdb-io/drawdb.git ~/drawdb
cd ~/drawdb
git log -1 --format='%H %cd'

Iqra wrote the commit hash into the wiki. drawDB moves quickly and publishes few formal releases, so the commit is the version. She checked out that exact commit so that a later rebuild could not pull in surprises.

git checkout 7683bdf90a8db5d284dc6b2bc545831aa4b6dd76
docker build -t studio/drawdb:7683bdf .
docker run -d --name drawdb --restart unless-stopped \
  -p 127.0.0.1:3000:80 studio/drawdb:7683bdf

The port is bound to 127.0.0.1, so only Caddy on the same server can reach it.

curl -sI http://127.0.0.1:3000 | head -1

The answer was HTTP/1.1 200 OK.


7. Or Skipping Docker Entirely

Ayisha asked whether they needed a container at all, since the output is only files. They did not.

cd ~/drawdb
npm ci
npm run build
sudo mkdir -p /srv/drawdb
sudo cp -r dist/* /srv/drawdb/

Caddy can serve that folder directly. The one line people miss is try_files.

schema.studio.example {
    root * /srv/drawdb
    try_files {path} /index.html
    file_server
    encode gzip
}

drawDB is a single-page application. Addresses like /editor are handled by JavaScript in the browser, not by files on disk. Without try_files, the first visit to / works, but refreshing the page while you are in the editor returns a 404, because there is no file called editor in the folder. With it, every unknown path gets index.html, and the application takes over from there.

The studio kept the Docker version, because every other service on the box was a container and the upgrade routine was already written for containers. Both ways are fine.


8. Putting Caddy in Front, With a Lock

For the Docker version, the Caddy block was short.

schema.studio.example {
    basic_auth {
        studio $2a$14$REPLACE_WITH_THE_HASH_FROM_CADDY_HASH_PASSWORD
    }
    reverse_proxy 127.0.0.1:3000
}
caddy hash-password
sudo systemctl reload caddy

The basic auth block is the same lock from chapter 3. drawDB has no login of its own, because it has nothing to protect on the server. The lock is there to keep the address from being found and used by strangers, and to keep the client's IT head happy when he asked who could reach it.

Iqra opened https://schema.studio.example, typed the shared password once, and the browser remembered it. The editor loaded.


9. Taking Out the Analytics

Iqra opened the browser's network panel on the first load, out of habit from the bank. One request went to a third-party analytics service. The public drawDB website uses it to count visitors, and the code that loads it is part of the application.

For a tool the studio hosted for itself, that request had no purpose. It also would have been one more line on the clinic's vendor questionnaire.

The analytics component is loaded in one file, src/main.jsx. She removed it before building.

sed -i '/@vercel\/analytics/d; /<Analytics/d' src/main.jsx
git diff --stat

The diff touched one file and removed two lines. She rebuilt the image, reloaded the page, and the network panel showed only requests to schema.studio.example.

She wrote the patch into a small script, build-drawdb.sh, so it would be applied on every rebuild and nobody had to remember it.


10. Living Under a Sub-Path

A month later, another team wanted to put drawDB under a path on an existing site, at /database-designer/, instead of on its own subdomain. Iqra helped them, and learned that this is harder than it looks.

The application assumes it lives at the root of the domain. Two things have to change.

The asset paths. Vite writes absolute paths like /assets/index.js into index.html. The --base option changes that prefix at build time.

npx vite build --base /database-designer/ --outDir dist

The routes. The React router and a handful of buttons that open new tabs use paths like /editor. The router needs a basename, and the buttons need the prefix added.

MOUNT=/database-designer
sed -i "s#<BrowserRouter>#<BrowserRouter basename=\"$MOUNT\">#" src/App.jsx
grep -rn 'window.open("/' src/

The grep showed every place that opened a root-relative path. Each one got the same prefix with a small sed line in the build script.

"If you can use a subdomain, use a subdomain," Iqra told them. "Every patch is something you have to check again when you upgrade."


11. Drawing the First Diagram From Real SQL

The studio's first real use was not drawing. It was reading.

Iqra dumped the schema of the staging database, without any data.

pg_dump --schema-only --no-owner --no-privileges \
  -h localhost -U clinic clinic_staging > clinic-schema.sql

In drawDB she created a new diagram, chose PostgreSQL as the database, and used Import from SQL. She pasted the CREATE TABLE statements.

drawDB drew nineteen tables. It placed them in a grid, with lines for every foreign key it found.

The importer reads table and column definitions and foreign keys. A pg_dump file also contains functions, triggers, sequences ownership, SET statements and comments, and the importer does not need those. When the first import failed on a statement it did not understand, Iqra removed everything except the CREATE TABLE, CREATE TYPE and ALTER TABLE ... ADD CONSTRAINT ... FOREIGN KEY lines, and the second import worked.

Then she spent forty minutes dragging tables into an order that made sense, and drew three subject areas around them.

Area Tables
Clinical patients, visits, eye_exams, prescriptions, prescription_items
Scheduling doctors, clinics, doctor_schedules, appointments, appointment_slots
Billing invoices, invoice_lines, payments, refunds, insurance_claims

The remaining four tables, users, roles, audit_log and settings, went into a corner with a note that said Platform. Not clinical. Do not join from reports.

Ayisha came over with her tea and looked at the screen for a while.

"I did not know refunds pointed at payments and not at invoices," she said.

"Neither did I, until it drew the line," Iqra said.


12. Designing the Next Change on the Canvas

The clinics wanted a new feature. A doctor could refer a patient to another doctor in a different clinic, for a retina scan or a surgery consult, and the receiving clinic should see the referral in its queue.

Before writing any code, Iqra drew it.

She added a referrals table with these fields.

Field Type Notes
id bigserial Primary key
patient_id bigint Not null
from_doctor_id bigint Not null
to_clinic_id bigint Not null
to_doctor_id bigint Nullable, the receiving clinic may assign later
reason referral_reason An enum
status referral_status An enum, default pending
created_at timestamptz Default now()

She created the two enums in the Enums tab, referral_reason with retina, glaucoma, cataract_surgery and other, and referral_status with pending, accepted, declined and completed.

Then she dragged four relationships. From patient_id to patients.id, from from_doctor_id to doctors.id, from to_clinic_id to clinics.id, and from to_doctor_id to doctors.id. On each one she set the on delete action. Patients and doctors were never hard-deleted in this system, so she chose RESTRICT, which makes the database refuse to delete a row that a referral still points to.

The Issues panel showed one warning. She had first given to_clinic_id the type int, while clinics.id was bigint. She fixed the type and the warning went away.

The whole team looked at the diagram in the Thursday meeting on the big screen. Ayisha asked why to_doctor_id was nullable. The junior developer asked whether a patient could have two open referrals to the same clinic. That question became a note on the canvas, and later a partial unique index.


13. From Diagram to Migration

When the design was agreed, Iqra used Export, SQL. drawDB wrote the PostgreSQL for the whole diagram, including the new table and the two enums.

She did not run it.

She copied only the parts for the new objects into a new migration file and read every line. Three things needed changing, and they always do.

  1. Index names. The export used names she would not have chosen. She renamed them to match the project's pattern, ix_<table>_<column>.

  2. The partial unique index. drawDB draws tables and columns, not every kind of index. She wrote this one by hand.

    CREATE UNIQUE INDEX ux_referrals_open_per_clinic
      ON referrals (patient_id, to_clinic_id)
      WHERE status IN ('pending', 'accepted');
    
  3. Order. The enums had to be created before the table that used them. The export already had them in the right order, but she checked.

The migration went through the normal review, ran on staging, and the next pg_dump --schema-only matched the diagram.

"Treat the export like a colleague's first draft," she wrote in the wiki. "It saves you the typing. It does not save you the reading."


14. The Afternoon the Diagrams Vanished

Three weeks in, Ayisha's laptop started showing a full disk warning. She opened the browser settings, found the storage page, and cleared all site data for every site she had not visited that week.

drawDB was one of them. The next morning, her copy of the clinic diagram, with two days of notes about the billing tables, was gone. There was no server copy, because there is no server copy.

Iqra's copy was fine, because she had drawn hers in a different browser on a different laptop. Ayisha's notes were lost.

That afternoon, the studio made a rule.

The diagram file lives in git, next to the migrations.

drawDB can export the whole diagram, with positions, colours, areas and notes, as a file that it can import again later. Iqra exported it and committed it.

db/
  migrations/
    0041_referrals.sql
  diagram/
    clinic.json
    clinic.png

The rule had three lines.

  1. Before you change the diagram, import db/diagram/clinic.json from the main branch, so you start from the latest copy.
  2. When you are done, export it again over the same file, export a PNG next to it, and commit both in the same pull request as the migration.
  3. Never keep the only copy of anything in a browser.

The PNG was for people who wanted to look without opening the editor. GitHub showed it inline in the pull request, so reviewers could see the new table and its lines next to the SQL that created it.

The clinic.json file was text, so the pull request also showed what changed in the diagram. It was not always pretty to read, but a new table showed up clearly as a new block.


15. What About the Share Button

drawDB has a Share button. On the public site, it creates a link that anyone can open to see your diagram.

Iqra tried it on the self-hosted copy and it failed. She read the code to find out why.

Sharing is the one feature that needs a server. The public drawDB site uses a small separate backend that stores the shared diagram, using a GitHub gist under the project's account. The editor alone does not include that backend.

The studio had three choices.

Choice What it means
Run the separate drawDB server too Shared diagrams go to gists under a GitHub account you control
Use the public share service The diagram goes to someone else's server, which was the problem they started with
Do not share links. Share the file in git. Everyone already had the repository

They chose the third. Nobody missed the button.


16. The AGPL Part

drawDB is licensed under the AGPL-3.0. For a team hosting it for themselves, the licence asks very little. Two parts are worth knowing.

  • If you only run it, unchanged, for yourself or your team, you are using it like any other open-source program.
  • If you modify it and let other people use your modified version over a network, the AGPL asks you to offer those users the source code of your modified version.

The studio's copy was modified. Iqra had removed the analytics lines. And the other team's copy under /database-designer/ had path patches too, and was used by people outside that team.

So both copies got a small page linked from the footer, called Source. It named the upstream repository, the exact commit they built from, and the list of changes they made, which was the sed lines from the build script. Anyone who wanted the source could rebuild the same thing from those three pieces of information.

It took fifteen minutes and closed the question for good.


17. Upgrading

Because the commit was the version, upgrading was a short, planned routine.

  1. Read the commits since the pinned one, looking for changes to the import, export or file format.
  2. Export every diagram the team cared about to its git folder. It should already be there, but check.
  3. Change the commit in build-drawdb.sh and rebuild the image.
  4. Run the build script's patches, and check that each sed line still matched something. A line that matches nothing does not fail. It silently does nothing.
  5. Replace the container and open the editor.
  6. Import clinic.json and export SQL. Compare the SQL with the previous export.

Step 4 caught a problem on their second upgrade. A file had been renamed upstream, the analytics line had moved, and the old sed matched nothing. Iqra added a check to the script so that it would stop if the analytics import was still present after patching.

if grep -q '@vercel/analytics' src/main.jsx; then
  echo "analytics still present, check the patch" >&2
  exit 1
fi

18. When Things Go Wrong

Iqra added a page to the studio wiki.

Symptom Likely cause
Blank page on a refresh inside the editor The web server is not sending index.html for unknown paths. Add try_files.
Page loads with no styles, assets return 404 Built for the root but served under a sub-path. Rebuild with --base.
Diagram missing on another laptop Diagrams live in each browser. Import the file from git.
Diagram gone after clearing browser data Same cause. The git copy is the real copy.
Work lost after closing a private window Private windows throw away their storage. Use a normal window.
Share button fails The editor has no sharing backend. Share the file instead.
SQL import fails on a pg_dump file Keep only the table, type and foreign key statements
Exported SQL has the wrong types The diagram was created for a different database. Check the database chosen for the diagram.
A request to an analytics service appears The analytics patch did not apply on this build

19. Epilogue: The Questionnaire, Answered

In November, Ayisha filled in the clinic group's vendor questionnaire again.

Under third-party services that store system design or data, she removed the diagram tool. She wrote one line instead. Database diagrams are drawn in a self-hosted editor that stores nothing on its server. The diagram file is kept in our private repository with the code.

The IT head called her about something else, and at the end of the call he asked about that line. She shared her screen, opened schema.studio.example, imported clinic.json and showed him the referral tables, with the subject areas in colour and the note in the corner about the platform tables.

He asked if they could have a PNG of it for the clinics' own documentation. Ayisha exported one while he waited.

That evening Iqra took down the photo of the whiteboard that had been pinned in the WhatsApp group for a year. In its place she pinned a link to the db/diagram/ folder.

Somebody erased the whiteboard the next morning. Nobody asked about it.


Quick Reference Cheat-Sheet

I want to Do this
Get the source git clone https://github.com/drawdb-io/drawdb.git and check out a pinned commit
Build with Docker docker build -t studio/drawdb:<commit> .
Run it privately docker run -d -p 127.0.0.1:3000:80 studio/drawdb:<commit>
Build plain files npm ci && npm run build, then serve dist/
Fix 404 on refresh try_files {path} /index.html in Caddy
Keep strangers out basic_auth in the Caddy block
Remove analytics Delete the analytics lines from src/main.jsx before building
Serve under a path vite build --base /path/ and set the router basename
Draw an existing database pg_dump --schema-only, keep the CREATE TABLE lines, Import from SQL
Get a migration draft Export, SQL, then copy and review only the new parts
Never lose a diagram Export the diagram file into git next to the migrations
Share a diagram Commit the file and a PNG. The Share button needs a separate backend.
Meet the AGPL Publish the upstream URL, the commit and your patches if you serve a modified copy to others
Upgrade Export diagrams, change the commit, rebuild, check every patch still applied

Key takeaways

  1. drawDB is a static web application. Build it once and any web server can host it. There is no database and no account.
  2. Diagrams live in the browser. That keeps them private, and it also means one cleared cache can delete them.
  3. The diagram file belongs in git. Export it next to the migrations, with a PNG, in the same pull request.
  4. Import first, draw second. Reading your real schema into the diagram shows you relationships you forgot you had.
  5. The SQL export is a first draft. It saves typing. You still read and adjust every line.
  6. Small patches, written down. Removing analytics or adding a sub-path is a few sed lines, kept in a script and listed on a source page.

A database diagram is only useful if it matches the database. Keep the tool close, keep the file in git, and the picture stops lying.