From am-plugin
Use this skill whenever the user wants to design, create, edit, or visualize a database schema using DBML (Database Markup Language). Triggers include: any mention of 'dbml', 'database schema', 'ERD', 'entity relationship diagram', 'table structure', 'data model', 'database design', 'db schema', or requests to model tables, relationships, foreign keys, or database architectures. Also use when converting SQL DDL to DBML, generating schemas from descriptions, or when the user has the DBML Live Preview VS Code extension installed. Do NOT use for writing raw SQL queries, Prisma schemas, actual database migrations, or ORM model code.
How this skill is triggered — by the user, by Claude, or both
Slash command
/am-plugin:dbmlThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
DBML (Database Markup Language) is a human-readable DSL for defining database structures. Files use the `.dbml` extension and can be visualized as ERD diagrams in VS Code via the **DBML Live Preview** extension (`nicolas-liger.dbml-viewer`).
DBML (Database Markup Language) is a human-readable DSL for defining database structures. Files use the .dbml extension and can be visualized as ERD diagrams in VS Code via the DBML Live Preview extension (nicolas-liger.dbml-viewer).
DBML is database-agnostic: focus on structure, not SQL dialect specifics.
| Task | Approach |
|---|---|
| New schema from scratch | Write .dbml file following structure below |
| Convert SQL → DBML | Translate CREATE TABLE / ALTER TABLE into DBML syntax |
| Convert description → DBML | Extract entities, attributes, relationships from natural language |
| Edit existing schema | Parse .dbml file, modify tables/refs, write back |
Every .dbml file follows this general order:
// 1. Project definition (optional but recommended)
Project project_name {
database_type: 'PostgreSQL'
Note: 'Description of the project'
}
// 2. Enum definitions
Enum status_type {
active
inactive
archived
}
// 3. Table definitions
Table users {
id integer [pk, increment]
name varchar [not null]
status status_type [default: 'active']
created_at timestamp [default: `now()`]
}
// 4. Standalone relationships (alternative to inline refs)
Ref: posts.user_id > users.id
// 5. Table groups (optional)
TableGroup core {
users
posts
}
Always start with a Project block to give context:
Project ecommerce {
database_type: 'PostgreSQL' // or 'MySQL', 'SQLite', 'SQL Server'
Note: '''
# E-Commerce Database
Designed for a multi-tenant SaaS platform.
Last updated: 2025-01.
'''
}
Table schema_name.table_name as Alias [headercolor: #3498DB] {
column_name column_type [settings]
}
schema_name is optional (defaults to public)as Alias is optional — useful for shortening long names in Refsheadercolor changes the table header color in the ERD visualizationTable users {
id integer [pk]
Note: 'Stores all registered users'
}
Table example {
column_name column_type [setting1, setting2, ...]
}
Use any standard SQL type as a single word. If the type contains spaces, wrap it in double quotes.
| Category | Types |
|---|---|
| Integer | integer, int, smallint, bigint, serial, bigserial |
| Decimal | decimal(10,2), numeric, float, double, real, money |
| String | varchar, varchar(255), char(10), text, citext |
| Boolean | boolean, bool |
| Date/Time | date, time, timestamp, timestamptz, interval |
| Binary | bytea, blob, binary |
| JSON | json, jsonb |
| UUID | uuid |
| Array | "integer[]", "varchar[]" |
| Custom | "bigint unsigned", any enum name |
| Setting | Description | Example |
|---|---|---|
pk or primary key | Primary key | id integer [pk] |
increment | Auto-increment | id integer [pk, increment] |
not null | NOT NULL constraint | name varchar [not null] |
null | Explicitly nullable | bio text [null] |
unique | Unique constraint | email varchar [unique, not null] |
default: value | Default value | status varchar [default: 'active'] |
note: 'text' | Column note/description | role varchar [note: 'User role'] |
ref: > table.col | Inline relationship | user_id int [ref: > users.id] |
Table example {
// Number
rating integer [default: 10]
// String (single quotes)
source varchar [default: 'direct']
// Expression (backticks)
created_at timestamp [default: `now()`]
valid_until date [default: `now() + interval '30 days'`]
// Boolean / null
is_active boolean [default: true]
deleted_at timestamp [default: null]
}
| Symbol | Meaning | Example |
|---|---|---|
> | Many-to-one | posts.user_id > users.id |
< | One-to-many | users.id < posts.user_id |
- | One-to-one | users.id - profiles.user_id |
<> | Many-to-many | students.id <> courses.id |
1. Inline (in column settings):
Table posts {
id integer [pk]
user_id integer [ref: > users.id]
}
2. Standalone Ref:
Ref: posts.user_id > users.id
3. Standalone named Ref with block:
Ref user_posts {
posts.user_id > users.id
}
Ref: posts.user_id > users.id [delete: cascade, update: no action]
Available actions: cascade, restrict, set null, set default, no action
Ref: order_items.(order_id, product_id) > products.(order_id, id)
DBML supports direct many-to-many with <>, but for explicit junction tables:
Table students_courses {
student_id integer [ref: > students.id]
course_id integer [ref: > courses.id]
indexes {
(student_id, course_id) [pk]
}
}
Table bookings {
id integer [pk]
country varchar
booking_date date
created_at timestamp
indexes {
created_at [name: 'idx_created_at', note: 'Date index']
booking_date [type: hash]
(country, booking_date) [unique]
(id, country) [pk] // composite primary key
(`lower(country)`) // expression index
}
}
| Setting | Description |
|---|---|
name: 'index_name' | Custom index name |
unique | Unique index |
pk | Primary key (for composite PKs) |
type: btree or type: hash | Index type |
note: 'text' | Index description |
Enum order_status {
pending [note: 'Order just placed']
processing [note: 'Payment confirmed']
shipped
delivered
cancelled
}
Table orders {
id integer [pk]
status order_status [not null, default: 'pending']
}
For enums in a specific schema:
Enum v2.order_status {
pending
shipped
}
If enum values contain spaces or special characters, use double quotes:
Enum color {
"bright red"
"dark blue"
}
Groups are purely visual — they cluster tables together in the ERD:
TableGroup core_tables [color: #2196F3] {
users
profiles
roles
}
TableGroup content_tables {
posts
comments
tags
}
Define common columns once, inject into multiple tables:
TablePartial timestamps {
created_at timestamp [not null, default: `now()`]
updated_at timestamp [not null, default: `now()`]
}
TablePartial soft_delete {
deleted_at timestamp [null]
is_deleted boolean [default: false]
}
Table users {
id integer [pk, increment]
name varchar [not null]
~timestamps // injects created_at and updated_at
~soft_delete // injects deleted_at and is_deleted
}
Table posts {
id integer [pk, increment]
title varchar [not null]
~timestamps
}
Table users {
id integer [pk]
Note: '''
# Users Table
Contains all registered users.
- Soft-deleted users have `deleted_at` set
- Email must be verified before `is_active = true`
'''
}
Table orders {
total decimal(10,2) [note: 'Total in USD, before tax']
}
Free-floating notes not attached to any table:
Note single_line_note {
'This is a single line note'
}
Note detailed_note {
'''
# Architecture Decision
We use soft deletes across all tables.
'''
}
// Single-line comment
Table users { // inline comment
id integer [pk]
}
Note: DBML does NOT support multi-line /* */ comments.
Table tenants {
id uuid [pk, default: `gen_random_uuid()`]
name varchar [not null]
slug varchar [unique, not null]
plan varchar [default: 'free']
created_at timestamp [default: `now()`]
}
Table users {
id uuid [pk, default: `gen_random_uuid()`]
tenant_id uuid [not null, ref: > tenants.id]
email varchar [unique, not null]
role varchar [not null, default: 'member']
created_at timestamp [default: `now()`]
indexes {
(tenant_id, email) [unique]
}
}
Table users {
id integer [pk, increment]
email varchar [unique, not null]
password_hash varchar [not null]
}
Table roles {
id integer [pk, increment]
name varchar [unique, not null]
}
Table permissions {
id integer [pk, increment]
name varchar [unique, not null, note: 'e.g. users:read, posts:write']
}
Table user_roles {
user_id integer [ref: > users.id]
role_id integer [ref: > roles.id]
indexes {
(user_id, role_id) [pk]
}
}
Table role_permissions {
role_id integer [ref: > roles.id]
permission_id integer [ref: > permissions.id]
indexes {
(role_id, permission_id) [pk]
}
}
Enum order_status {
pending
paid
shipped
delivered
refunded
}
Table products {
id integer [pk, increment]
name varchar [not null]
price decimal(10,2) [not null]
stock integer [not null, default: 0]
}
Table orders {
id integer [pk, increment]
user_id integer [not null, ref: > users.id]
status order_status [not null, default: 'pending']
total decimal(10,2) [not null]
created_at timestamp [default: `now()`]
}
Table order_items {
id integer [pk, increment]
order_id integer [not null, ref: > orders.id]
product_id integer [not null, ref: > products.id]
quantity integer [not null, default: 1]
unit_price decimal(10,2) [not null]
}
snake_case, plural (users, order_items)snake_case, singular (user_id, created_at)snake_case (order_status, user_role)referenced_table_singular_id (user_id, order_id)table1_table2 alphabetically (courses_students)timestamps, soft_delete, audit_fields)not null on columns that should never be emptydefault values where appropriate[note: ...] on non-obvious columnsBefore finalizing a .dbml file, verify:
[pk] or composite via indexes)'...' or triple single quotes '''...'''/* */ multi-line comments (use // only)~partial_namedefault: 'value'default: \now()``> many-to-one, < one-to-many, - one-to-one, <> many-to-many).dbml extension (e.g., schema.dbml)npx claudepluginhub astyanm/claude-am-plugin --plugin am-pluginDesigns complete database schemas with tables, fields, indexes, ER diagrams for MySQL, PostgreSQL, SQL Server. Generates Markdown docs, SQL scripts, DrawDB JSON/DBML files.
Designs production-ready database schemas with tables, fields, indexes, ER diagrams, Markdown docs, SQL scripts, and DrawDB/DBML files for MySQL, PostgreSQL, SQL Server.
Generates ERDs, Mermaid/PlantUML diagrams, schema docs, and insights from SQL dumps, ORM models (Prisma, SQLAlchemy, TypeORM), migrations, or live DBs.