How DrawDB Parses and Exports DBML Format: A Deep Dive into the Source Code

DrawDB uses two pure utility modules—fromDBML in src/utils/importFrom/dbml.js for parsing and toDBML in src/utils/exportAs/dbml.js for exporting—to convert between DBML text and its internal diagram model via the @dbmljs/parser library.

DrawDB treats DBML (Database Markup Language) as a first-class format for database diagrams. The open-source diagramming tool implements bidirectional conversion through isolated utility functions that transform plain-text DBML into structured JavaScript objects and back. This article examines the exact parsing and serialization logic implemented in the drawdb-io/drawdb repository.

Parsing DBML with fromDBML

The import utility located at src/utils/importFrom/dbml.js exposes a single primary function fromDBML(src, database) that transforms raw DBML strings into DrawDB's internal diagram representation. This module acts as a pure function, accepting source text and an existing database object, then returning a structured payload ready to merge into the application state.

Tokenization and AST Generation

The parsing process begins by forwarding the raw DBML string to the @dbmljs/parser library. The parser's parse(src) method returns a structured Abstract Syntax Tree (AST) describing tables, enums, relationships, and notes. This AST serves as the intermediate representation that the utility walks to construct internal objects.

Building Internal Objects

For each table node in the AST, the code instantiates a new Table object in the current diagram, setting its name and metadata. The function then iterates over the table's columns array to create Column objects, preserving type definitions, nullability constraints, default values, and primary key flags. Similarly, enum nodes convert to Enum objects with each entry becoming an EnumValue.

Resolving Relationships and References

The parser handles ref blocks by constructing Relationship objects that resolve source and target table-column identifiers. It determines cardinality patterns and stores custom names or comments associated with the foreign key constraints. All resolved references attach directly to the database object passed into the function, ensuring the returned structure mirrors the internal diagram representation.

import { fromDBML } from '@/utils/importFrom/dbml';
import { useDiagram } from '@/hooks/useDiagram';

// `rawDbml` is a string read from a file or textarea
const rawDbml = `
Table users {
  id int [pk, increment]
  name varchar
}
`;

const { diagram, setDiagram } = useDiagram();

// Convert DBML → internal model
const parsed = fromDBML(rawDbml, diagram.database);
setDiagram(prev => ({
  ...prev,
  ...parsed,                 // merges tables, enums, relationships, etc.
}));

Exporting Diagrams to DBML with toDBML

The export utility in src/utils/exportAs/dbml.js provides the toDBML(diagram) function, which performs the inverse operation. This serializer walks the diagram's internal model and emits valid DBML syntax, preserving element order, comments, and inline annotations.

Serializing Tables and Constraints

For each table in the diagram, the generator emits a Table "<name>" { block followed by column definitions. Columns include type specifications and constraint annotations such as [pk], [ref: > other.table.id], null, or default modifiers. The serializer maintains the exact syntax required for DBML compatibility, including proper quoting and indentation.

Handling Enums and Relationships

Enum definitions render as Enum "<name>" { blocks containing individual value lines. Relationships convert to Ref: <src> <-> <dst> syntax, with arrow directions (<, >, -, <>) explicitly expressing cardinality between entities. The function ensures that all referential integrity constraints map correctly to DBML's reference syntax.

Preserving Comments and Annotations

Table-level notes and diagram-level comments serialize as DBML comment lines (// …). The utility captures any stray annotations attached to model elements during the walk, ensuring that metadata survives the round-trip conversion from internal model to text format.

import { toDBML } from '@/utils/exportAs/dbml';
import { useDiagram } from '@/hooks/useDiagram';

const { diagram } = useDiagram();

// Serialize diagram → DBML string
const dbmlString = toDBML(diagram);

// Download as a .dbml file
const blob = new Blob([dbmlString], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'diagram.dbml';
a.click();

Integration with the UI Layer

Both utilities operate as pure functions without direct UI dependencies. The DBMLEditor.jsx component in src/components/EditorSidePanel/ calls toDBML when displaying generated DBML in the side panel or triggering downloads. Conversely, ImportDiagram.jsx in src/components/EditorHeader/Modal/ invokes fromDBML when users upload DBML files, merging the returned structure into the current application state through React hooks.

Summary

  • Import pipeline: src/utils/importFrom/dbml.js uses @dbmljs/parser to tokenize DBML text, then walks the AST to instantiate Table, Column, Enum, and Relationship objects.
  • Export pipeline: src/utils/exportAs/dbml.js traverses the internal diagram model to emit valid DBML syntax with proper constraint notation ([pk], [ref: > ...]) and cardinality arrows.
  • Pure functions: Both fromDBML and toDBML remain isolated from UI concerns, accepting and returning plain JavaScript objects.
  • UI integration: Components like DBMLEditor.jsx and ImportDiagram.jsx handle file I/O and state management while delegating format conversion to these utilities.

Frequently Asked Questions

How does DrawDB handle DBML parsing errors?

DrawDB delegates initial tokenization to the @dbmljs/parser library, which throws descriptive syntax errors for malformed DBML. The fromDBML function does not implement custom error recovery; instead, it allows parser exceptions to propagate upward, where UI components like ImportDiagram.jsx can catch and display user-friendly error messages in the interface.

Can DrawDB preserve custom DBML comments during import and export?

Yes. During import, the parser attaches stray comments and table-level notes to the corresponding model elements. When exporting, toDBML serializes these annotations as standard DBML comment lines (// …). This ensures that documentation and inline notes survive round-trip conversions between the text format and the internal diagram representation.

What DBML relationship cardinalities does DrawDB support?

DrawDB supports all standard DBML cardinality indicators through the toDBML serializer. The export utility renders relationships using directional arrows including < (many-to-one), > (one-to-many), - (one-to-one), and <> (many-to-many), preserving the exact semantics defined in the diagram's internal Relationship objects.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →