DBML Guide: Database Markup Language Explained
DBML is a concise text language for defining database schemas that render as diagrams. This guide covers its syntax for tables, references, enums, and indexes with working examples.
DBML, short for Database Markup Language, is a small text language designed to do one thing well: describe a database schema concisely enough to type by hand, precisely enough to render as an ERD and generate SQL. For engineers who prefer writing to dragging, it hits a sweet spot. You define your tables and their relationships in a readable text file, and a diagram appears automatically, with the added benefits that text versions cleanly in Git and diffs are meaningful in code review.
This guide covers DBML's core syntax, tables and columns, references, enums, and indexes, with working examples you can adapt. Even if you use a canvas tool most of the time, understanding DBML is useful because it is a lingua franca for schema-as-code and a clean way to sketch a design quickly. You can render DBML-style schemas and work alongside them in Atlas Diagram Studio at /diagrams, and the ERD tooling at /diagram-tools/erd-tool covers the visual side.
Tables and columns
The heart of DBML is the Table block. You declare a table, then list its columns with names and types, optionally with settings in square brackets. A basic table looks like this: `Table users { id integer [primary key] email varchar [unique, not null] name varchar created_at timestamp }` Each line is a column: a name, a type, and optional settings. The settings in brackets, like [primary key], [unique], and [not null], map directly onto the constraints you would write in SQL, which is what lets DBML generate real DDL.
The column settings cover the essentials of schema definition compactly. [primary key] or [pk] marks the identifier, [unique] enforces a natural key, [not null] makes a column required, and [default: 'value'] sets a default. You can combine them in one bracket, as with [unique, not null]. This terseness is the appeal: a column that would take a verbose line of SQL is a short, scannable DBML line, and a whole table fits in a few lines you can take in at a glance.
References: defining relationships
Relationships in DBML are called references, and there are two ways to write them. The inline style adds a ref setting to a foreign key column: `author_id integer [ref: > users.id]` on a posts table. The symbol in the ref tells you the cardinality: > means many-to-one (many posts to one user), < means one-to-many, - means one-to-one, and <> means many-to-many. So [ref: > users.id] reads as "many of this table to one users row."
The alternative is a standalone Ref block, which some people find clearer for keeping relationships separate from column definitions: `Ref: posts.author_id > users.id` declares the same relationship. Either style renders as a relationship line on the ERD with the correct crow's foot cardinality. For a many-to-many relationship you can either declare it directly with the <> operator or, more explicitly, model a junction table with two many-to-one references, which is the better choice when the relationship carries its own columns.
Enums, indexes, and organization
DBML supports enums for columns with a fixed set of allowed values. You define an Enum block, then use it as a column type: `Enum order_status { pending shipped delivered cancelled }` and then a column `status order_status`. This is cleaner than a stringly-typed column and documents the valid states directly in the schema. Enums render on the diagram and generate the corresponding database enum or check constraint.
For performance, DBML lets you declare indexes inside a table using an Indexes block, marking single or composite indexes and which are unique. You can also add Note annotations to tables and columns for documentation that travels with the schema, and TableGroup blocks to visually cluster related tables on the diagram. These organizational features matter more than they sound: on a large schema, grouping related tables and documenting non-obvious columns is the difference between a diagram that clarifies and one that overwhelms.
Why use DBML
DBML earns its place in a specific workflow, and it is worth being clear about when it shines and when a canvas serves you better.
- It is fast to write for people who think in tables and columns rather than shapes.
- As plain text, it versions in Git and produces meaningful diffs in code review.
- It renders to an ERD automatically, so layout is one less thing to manage.
- It generates SQL DDL for multiple databases, bridging design and implementation.
- Enums, indexes, and notes let the text capture real schema detail, not just boxes and lines.
- It is less suited to visual polish, mixed diagram types, or non-technical collaborators, where a canvas tool fits better.