# RelSpec [![Release](https://img.shields.io/gitea/v/release/wdevs/relspecgo?gitea_url=https://git.warky.dev&label=release)](https://git.warky.dev/wdevs/relspecgo/releases/latest) [![CI](https://git.warky.dev/wdevs/relspecgo/actions/workflows/ci.yml/badge.svg)](https://git.warky.dev/wdevs/relspecgo/actions/workflows/ci.yml) [![Integration Tests](https://git.warky.dev/wdevs/relspecgo/actions/workflows/integration-tests.yml/badge.svg)](https://git.warky.dev/wdevs/relspecgo/actions/workflows/integration-tests.yml) [![Go Version](https://img.shields.io/badge/go-1.24.0-blue.svg)](https://go.dev/dl/) [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE) > Bidirectional database schema conversion, validation, and templating tool. ![RelSpec](./assets/image/relspec1_512.jpg) ## Install ```bash 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` | See [docs/FORMAT_EXAMPLES.md](docs/FORMAT_EXAMPLES.md) for usage examples covering every format. ## Commands ### `convert` — Schema conversion ```bash # 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 # Watch mode: regenerate whenever the source file(s) change (Ctrl-C to stop) relspec convert --from dbml --from-path schema.dbml --to gorm --to-path models/ --package models --watch ``` `--watch` works with `--from-path` and `--from-list` (not live database connections or `--dry-run`). Source files are polled every `--watch-interval` (default 500ms), a directory source is watched recursively, and the output path is ignored so generating into the source tree does not loop. Conversion errors are printed and watching continues. PostgreSQL connections opened by relspec set `application_name` by default to `relspecgo/` (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) ```bash # 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 ```bash # 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 ```bash relspec diff --from pgsql --from-conn "postgres://localhost/db1" \ --to pgsql --to-conn "postgres://localhost/db2" ``` ### `templ` — Custom template rendering ```bash # 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. ### `job` — Declarative job files Run named jobs from a `relspec.yml` manifest instead of repeating long command lines. ```bash # List jobs discovered in ./relspec.yml and ./relspec..yml (deterministic) relspec job list # Validate and print the plan without running anything relspec job run build-schema --plan # Run a job (and its declared dependencies) relspec job run build-schema ``` ```yaml # relspec.yml version: 1 jobs: build-schema: command: convert # closed allow-list: convert | merge | split | scripts-list | scripts-exec | templ | inspect | diff description: Merge the DBML sources and emit PostgreSQL DDL inputs: - path: schema/core.dbml format: dbml - path: schema/tenant.dbml format: dbml output: format: pgsql path: build/schema.sql overwrite: true options: flatten_schema: false logfile: .relspec/log/build-schema.log ``` The job system is **not** a shell: `command` is a fixed enum, every path is resolved relative to the job file and may not escape it, and remote database credentials are referenced by environment-variable name (`conn_env:`) and redacted from logs. The whole plan — unknown commands/formats, duplicate job names, missing inputs, path traversal, dependency cycles — is validated before any job runs. Path-like fields also support `${NAME}` environment-variable references, which are expanded and safety-checked during pre-flight. See [docs/JOB_FILES.md](docs/JOB_FILES.md). ### `edit` — Interactive TUI editor ```bash # 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+ ```bash 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`](./pkg/sqltypes/README.md) — 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](./pkg/sqltypes/README.md) for the full type reference, or the [`bun`](./pkg/writers/bun/README.md) / [`gorm`](./pkg/writers/gorm/README.md) 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`](./pkg/writers/bun/README.md#nullablearrays) flag for nullable-array handling. The `SqlXxxArray` wrapper types remain available in `pkg/sqltypes` and are still used by the `gorm` writer. #### Custom type mapping Override the built-in SQL → Go mapping of the `bun` and `gorm` writers with the repeatable `--type-map sqltype=gotype` flag: ```bash relspec convert --from pgsql --from-conn "$DSN" --to gorm --to-path models.go \ --type-map uuid=string --type-map jsonb=json.RawMessage ``` SQL type names are matched case-insensitively on the base type (modifiers such as `(10,2)` are ignored; aliases like `int4` resolve to `integer`). NOT NULL columns use the Go type verbatim, nullable columns get a `*` prefix (unless the type is already a pointer, slice, map or `any`), and arrays become `[]gotype`. Unmapped types keep their defaults. The flag does not add imports: use types that need none, or add the import afterwards (e.g. with `goimports`). ## Contributing 1. Register or sign in with GitHub at [git.warky.dev](https://git.warky.dev) 2. Clone the repository: `git clone https://git.warky.dev/wdevs/relspecgo.git` 3. Create a feature branch: `git checkout -b feature/your-feature-name` 4. Commit your changes and push the branch 5. Open a pull request with a description of the new feature or fix For questions or discussion, join the Discord: [discord.gg/74rcTujp25](https://discord.gg/74rcTujp25) — `warkyhein` ## Links - [Todo](./TODO.md) - [AI Use Policy](./AI_USE.md) - [License](LICENSE) — Apache 2.0 · Copyright 2025 Warky Devs