# 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. ### `batch` — Convert many inputs in one run Converts each input independently (one output per input, unlike `--from-list` which merges). `--input` takes paths or globs; outputs go to `--to-dir`. Use `--keep-going` to continue past failures (exit is still non-zero) and `--dry-run` to validate without writing. For named workflows see `relspec job run`. ```bash relspec batch --from dbml --input "schemas/*.dbml" --to json --to-dir out/ ``` 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