The Sequelforge DSL
A compact, declarative language for describing relational schemas. Inspired by DBML, with a few opinionated extensions. Write tables and references in plain text and get a live, animated ER diagram plus production-ready DDL for PostgreSQL, MySQL 8, SQL Server, Oracle, and SQLite.
Quickstart
Sequelforge schemas are plain text. You declare Tables, optionally connect them with References, and the diagram updates as you type. Here is the smallest useful schema:
Table users {
id uuid [pk]
email varchar(255) [unique, not null]
}Paste that into the editor and you immediately get a table card with a primary key and a unique, non-null column.
Tables
A table has a name and a body of columns. Each column is a name, a type, and an optional list of attributes in square brackets.
Table products {
id uuid [pk]
sku varchar(64) [unique, not null]
name varchar(160) [not null]
price_cents int [not null]
active boolean [not null, default: true]
}Tables can live in a schema and carry an alias for use in references:
Table shop.orders as O {
id uuid [pk]
}Column types
Any identifier is accepted as a type, so you are never blocked. These map to native PostgreSQL types on export, optionally with length/precision arguments like varchar(255) or numeric(10, 2).
Marking an integer column with increment exports it as SERIAL / BIGSERIAL.
Column attributes
Attributes go inside [ ], separated by commas. They fine-tune constraints and metadata for a column.
| pk | Marks the column as the primary key. |
| unique | Adds a UNIQUE constraint. |
| not null | Adds a NOT NULL constraint. |
| increment | Auto-increment — exported as SERIAL / BIGSERIAL. |
| default: <value> | Default literal or function call, e.g. default: now() or default: 'pending'. |
| note: "<text>" | Free-form comment, exported as a SQL COMMENT. |
| ref: <rel> table.column | Inline foreign key. The relationship operator is one of > < - <>. |
Table orders {
id uuid [pk]
user_id uuid [ref: > users.id, not null]
status order_status [not null, default: 'pending']
total_cents int [not null]
placed_at timestamptz [not null, default: now(), note: "UTC"]
}References & relationships
Relationships can be written inline on a column (ref:)or as a standalone Ref statement. The operator encodes the cardinality:
| > | Many-to-one — the source points to one target row. |
| < | One-to-many. |
| - | One-to-one. |
| <> | Many-to-many. |
// Inline, on the column:
Table posts {
id uuid [pk]
author uuid [ref: > users.id]
}
// Standalone, optionally named:
Ref fk_posts_author: posts.author > users.idCardinality — (min,max), Chen & Crow's Foot
Annotate a relationship with explicit (min,max) cardinalities per end, plus an optional label (the relationship verb). from is the source end (the table holding the foreign key), to is the target end. max may be a number, N, or * for “many”.
Table projects {
id uuid [pk]
owner uuid [ref: > employee.id,
from: (1,1), to: (0,*), label: "manages"]
}
// or on a standalone Ref:
Ref: projects.owner > employee.id [from: (1,1), to: (0,*), label: "manages"]Read it look-here: a project participates in (1,1) “manages” relationships (exactly one employee), while an employee participates in (0,*) (any number of projects). If you omit the cardinalities, Sequelforge derives sensible defaults from the operator and the not-null status of the foreign key.
Use the toolbar switch to flip the whole diagram between the two notations:
| Crow's Foot | Direct edge with crow's-foot markers derived from (min,max): a bar = one, a crow's foot = many; a circle = optional (min 0), a bar = mandatory (min ≥ 1). So (0,*) → ○<, (1,1) → ||. |
| Chen | The relationship becomes a labelled rhombus (◇) between the tables, with the (min,max) on each connecting edge. |
Referential actions
Standalone references accept delete and update actions, exported as ON DELETE / ON UPDATE clauses.
Ref: order_items.order_id > orders.id [delete: cascade]
Ref: orders.user_id > users.id [delete: restrict, update: no action]Valid actions: cascade, restrict, set null, set default, no action.
Generalization & specialization
A generalization relates a supertype to a set of subtypes (an IS-A hierarchy). Declare the supertype, then list its subtypes. Two constraints describe the set:
| disjoint | An instance belongs to at most one subtype (default). |
| overlapping | An instance may belong to several subtypes at once. |
| total | Every supertype instance must be in some subtype. |
| partial | A supertype instance need not be in any subtype (default). |
Table person {
id uuid [pk]
email varchar(255) [unique, not null]
}
Table employee { id uuid [pk] hired_on date }
Table customer { id uuid [pk] loyalty_points int }
Generalization person [disjoint, total] {
employee
customer
}In the diagram, subtypes connect into a circular hub marked d (disjoint) or o (overlapping); a total generalization doubles the hub's ring, and a hollow triangle points at the supertype. On export, each subtype key becomes a foreign key to the supertype key (table-per-type).
Weak entities
A weak entity has no identity of its own. It is identified by an owning (identifying) entity plus a partial key (discriminator). Prefix the table with Weak and name the owner with depends on:
Table projects {
id uuid [pk]
name varchar(160) [not null]
}
Weak Table task depends on projects {
project_id uuid [ref: > projects.id, not null]
number int [partial key]
title varchar(160) [not null]
}Weak entities are drawn with the classic ER double border, the partial key gets a dashed underline, and the identifying relationship to the owner is a double line. On export, the weak entity's primary key becomes the composite of the owner's foreign key and the partial key — here (project_id, number).
Indexes
Add an indexes block inside a table. Single or composite columns are supported, each with optional unique and a custom name.
Table orders {
id uuid [pk]
user_id uuid [not null]
placed_at timestamptz [not null]
indexes {
(user_id, placed_at) [name: 'idx_orders_user_placed']
user_id [unique]
placed_at
}
}Enums
Declare an enum and use its name as a column type. On export it becomes a PostgreSQL CREATE TYPE ... AS ENUM; the other dialects (MySQL 8, SQL Server, Oracle, SQLite) map it to an inline ENUM or a CHECK constraint.
Enum order_status {
pending
paid
shipped
cancelled [note: "terminal state"]
}Table groups
Groups are a visual aid — they bundle related tables together without affecting the generated SQL.
TableGroup commerce {
orders
order_items
products
}Full example
Everything together — the same schema that loads by default in the editor.
Enum employment_type {
full_time
part_time
contractor
}
Table person {
id uuid [pk]
email varchar(255) [unique, not null]
}
Table employee {
id uuid [pk]
hired_on date [not null]
kind employment_type [not null]
}
Table customer {
id uuid [pk]
loyalty_points int [not null, default: 0]
}
Generalization person [disjoint, total] {
employee
customer
}
Table projects {
id uuid [pk]
name varchar(160) [not null]
}
Weak Table task depends on projects {
project_id uuid [ref: > projects.id, not null]
number int [partial key]
title varchar(160) [not null]
}Migration timeline
Drop a set of Flyway migration scripts on /timeline and watch your schema evolve version by version: each migration is applied in order and the diagram animates from one version to the next, with a diff of what changed between them.
Files follow the standard Flyway naming convention V<version>__<name>.sql and are replayed in version order (so V2 runs before V10). All five SQL dialects — PostgreSQL, MySQL 8, SQL Server, Oracle, and SQLite — are supported, and the dialect is auto-detected, just like SQL import.
V1__init.sql
V2__add_orders.sql
V3__order_status_enum.sql
V10__drop_legacy_columns.sqlBetween any two versions, the diff highlights:
| Added | New tables, columns, enums, and references. |
| Dropped | Tables and columns removed by the migration. |
| Changed | Column type changes and altered constraints. |
Keyboard shortcuts
| ⌘ / Ctrl + S | Save the current diagram. |
| Tab | Indent inside the editor. |
| ⌘ / Ctrl + Z | Undo. |


Comments
Three comment styles are supported and ignored by the parser.