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

> Explore how DrawDB parses and exports DBML format by diving into its source code. Learn about the utility modules and the @dbmljs/parser library for seamless conversion.

- Repository: [drawDB/drawdb](https://github.com/drawdb-io/drawdb)
- Tags: deep-dive
- Published: 2026-08-11

---

**DrawDB uses two pure utility modules—`fromDBML` in [`src/utils/importFrom/dbml.js`](https://github.com/drawdb-io/drawdb/blob/main/src/utils/importFrom/dbml.js) for parsing and `toDBML` in [`src/utils/exportAs/dbml.js`](https://github.com/drawdb-io/drawdb/blob/main/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`](https://github.com/drawdb-io/drawdb/blob/main/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.

```javascript
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`](https://github.com/drawdb-io/drawdb/blob/main/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.

```javascript
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`](https://github.com/drawdb-io/drawdb/blob/main/DBMLEditor.jsx)** component in `src/components/EditorSidePanel/` calls `toDBML` when displaying generated DBML in the side panel or triggering downloads. Conversely, **[`ImportDiagram.jsx`](https://github.com/drawdb-io/drawdb/blob/main/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`](https://github.com/drawdb-io/drawdb/blob/main/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`](https://github.com/drawdb-io/drawdb/blob/main/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`](https://github.com/drawdb-io/drawdb/blob/main/DBMLEditor.jsx) and [`ImportDiagram.jsx`](https://github.com/drawdb-io/drawdb/blob/main/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`](https://github.com/drawdb-io/drawdb/blob/main/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.