Table.Columns/Constraints/Indexes/Relationships are Go maps, and every writer, reader, diff, inspector, and merge code path that iterated them directly was subject to Go's randomized map order, so identical input could produce different output (or a different in-report violation/diff order) on every run. Most visibly this showed up as bun/gorm `unique:` struct tags changing order across consecutive `make models` runs with no source change. Fixed by sorting map iteration (by Sequence then Name, or alphabetically for string-keyed maps) everywhere the order affects generated output or first-match tie-break logic, across the bun, gorm, sqlite, dbml, drawdb, pgsql, prisma, graphql, typeorm, drizzle, and dctx writers; the dctx, prisma, and typeorm readers; the shared models.GetPrimaryKey/ GetForeignKeys helpers; pkg/diff, pkg/inspector, and pkg/merge; and the TUI column/relationship pickers in pkg/ui.
RelSpec
Bidirectional database schema conversion, validation, and templating tool.
Install
go install -v git.warky.dev/wdevs/relspecgo/cmd/relspec@latest
Supported Formats
| Direction | Formats |
|---|---|
| Readers | bun dbml dctx drawdb drizzle gorm graphql json mssql pgsql prisma sqldir sqlite typeorm yaml |
| Writers | bun dbml dctx drawdb drizzle gorm graphql json mssql pgsql prisma sqlexec sqlite template typeorm yaml |
Commands
convert — Schema conversion
# PostgreSQL → GORM models
relspec convert --from pgsql --from-conn "postgres://user:pass@localhost/mydb" \
--to gorm --to-path models/ --package models
# DBML → PostgreSQL DDL
relspec convert --from dbml --from-path schema.dbml --to pgsql --to-path schema.sql
# PostgreSQL → SQLite (auto flattens schemas)
relspec convert --from pgsql --from-conn "postgres://..." --to sqlite --to-path schema.sql
# Multiple input files merged
relspec convert --from json --from-list "a.json,b.json" --to yaml --to-path merged.yaml
PostgreSQL connections opened by relspec set application_name by default to
relspecgo/<version> (with component suffixes internally, e.g. readers/writers).
If you need a custom value, provide application_name explicitly in the connection
string query parameters.
merge — Additive schema merge (never modifies existing items)
# Merge two JSON schemas
relspec merge --target json --target-path base.json \
--source json --source-path additions.json \
--output json --output-path merged.json
# Merge PostgreSQL into JSON, skipping tables
relspec merge --target json --target-path current.json \
--source pgsql --source-conn "postgres://user:pass@localhost/db" \
--output json --output-path updated.json \
--skip-tables "audit_log,temp_tables"
Skip flags: --skip-relations --skip-views --skip-domains --skip-enums --skip-sequences
inspect — Schema validation / linting
# Validate PostgreSQL database
relspec inspect --from pgsql --from-conn "postgres://user:pass@localhost/mydb"
# Validate DBML with custom rules
relspec inspect --from dbml --from-path schema.dbml --rules .relspec-rules.yaml
# JSON report output
relspec inspect --from json --from-path db.json --output-format json --output report.json
# Filter to specific schema
relspec inspect --from pgsql --from-conn "..." --schema public
Rules: naming conventions, PK/FK standards, missing indexes, reserved keywords, circular dependencies.
diff — Schema comparison
relspec diff --from pgsql --from-conn "postgres://localhost/db1" \
--to pgsql --to-conn "postgres://localhost/db2"
templ — Custom template rendering
# Render database schema to Markdown docs
relspec templ --from pgsql --from-conn "postgres://user:pass@localhost/db" \
--template docs.tmpl --output schema-docs.md
# One TypeScript file per table
relspec templ --from dbml --from-path schema.dbml \
--template ts-model.tmpl --mode table \
--output ./models/ --filename-pattern "{{.Name | toCamelCase}}.ts"
Modes: database (default) · schema · table · script
Template functions: string utils (toCamelCase, toSnakeCase, pluralize, …), type converters (sqlToGo, sqlToTypeScript, …), filters, loop helpers, safe access.
edit — Interactive TUI editor
# Edit DBML schema interactively
relspec edit --from dbml --from-path schema.dbml --to dbml --to-path schema.dbml
# Edit live PostgreSQL database
relspec edit --from pgsql --from-conn "postgres://user:pass@localhost/mydb" \
--to pgsql --to-conn "postgres://user:pass@localhost/mydb"
Development
Prerequisites: Go 1.24.0+
make build # → build/relspec
make test # race detection + coverage
make lint # requires golangci-lint
make coverage # → coverage.html
make install # → $GOPATH/bin
Project Structure
cmd/relspec/ CLI commands
pkg/readers/ Input format readers
pkg/writers/ Output format writers
pkg/inspector/ Schema validation
pkg/diff/ Schema comparison
pkg/merge/ Schema merging
pkg/models/ Internal data models
pkg/transform/ Transformation logic
pkg/pgsql/ PostgreSQL utilities
pkg/sqltypes/ Nullable SQL types for generated/hand-written models (see below)
Nullable Types (pkg/sqltypes)
The bun and gorm writers can generate model structs using
pkg/sqltypes — nullable types (SqlString,
SqlInt32, SqlTimeStamp, SqlStringArray, …) that implement
database/sql.Scanner, driver.Valuer, and JSON/YAML/XML marshalling in one
type, selected via --types sqltypes. See the
pkg/sqltypes README for the full type
reference, or the bun /
gorm writer docs for the --types flag
(sqltypes, stdlib, or baselib). PostgreSQL array columns are the one
exception: the bun writer always generates native Go slices ([]string,
[]int32, …) with an explicit array bun tag, regardless of --types —
see bun's --array-nullable
flag for nullable-array handling. The SqlXxxArray wrapper types remain
available in pkg/sqltypes and are still used by the gorm writer.
Contributing
- Register or sign in with GitHub at git.warky.dev
- Clone the repository:
git clone https://git.warky.dev/wdevs/relspecgo.git - Create a feature branch:
git checkout -b feature/your-feature-name - Commit your changes and push the branch
- Open a pull request with a description of the new feature or fix
For questions or discussion, join the Discord: discord.gg/74rcTujp25 — warkyhein
Links
- Todo
- AI Use Policy
- License — Apache 2.0 · Copyright 2025 Warky Devs



