Skip to Content

--driver

Selects the database engine the new project is wired against. Default is postgres. The flag is read once at scaffold time and propagated into every file that has a per-driver shape — config.yaml, compose.yaml, compose.production.yaml, .env.example, dev.dockerfile, README.md, and db/migrations/.

gofasta new myapp # postgres (default) gofasta new myapp --driver postgres gofasta new myapp --driver mysql gofasta new myapp --driver sqlite gofasta new myapp --driver sqlserver gofasta new myapp --driver clickhouse

Passing any other value fails fast with a clear error listing the accepted set. There is no “swap the driver later” command — the scaffolded files are driver-specific by design (different migration syntax per engine), so pick the driver you want up front.

Supported drivers

DriverDefault portCompose imageDB-level invariants
postgres5432postgres:18-alpineTriggers (shared PL/pgSQL functions) enforce updated_at, record_version++, and not-deletable
mysql3306mysql:8.4updated_at column attribute + per-table triggers for record_version++ and not-deletable
sqlite(file-based)— (no Compose service)Per-table triggers for all three invariants (AFTER UPDATE, BEFORE DELETE WHEN)
sqlserver1433mcr.microsoft.com/mssql/server:2022-latestCombined AFTER UPDATE trigger + INSTEAD OF DELETE trigger
clickhouse9000clickhouse/clickhouse-server:24.8-alpineApplication-layer only — ClickHouse has no row triggers

The three invariants the scaffold enforces on the users table (and that subsequent g scaffold <Resource> calls inherit) are:

  1. updated_at auto-touch — any UPDATE bumps the column to the current timestamp.
  2. record_version increment — any UPDATE increments the version counter, powering optimistic locking in pkg/models.
  3. Not-deletable guard — rows flagged is_deletable = false reject DELETEs at the DB layer.

These are defense-in-depth: they hold for any client (psql, mysql CLI, sqlcmd, admin GUIs, attackers with valid credentials), not just code that goes through the generated GORM repositories. The exception is ClickHouse, where the engine doesn’t support row triggers — the same invariants are enforced in app/repositories/<resource>.repository.go’s UpdateIfVersionMatches and equivalent methods.

What changes per driver

Each --driver value selects a different rendered output for these files:

config.yaml

The database: block carries the driver, host, port, and (Postgres only) sslmode. SQLite skips host/port entirely and uses name: <project>.db.

# --driver postgres database: driver: postgres host: localhost port: "5432" sslmode: disable
# --driver sqlite database: driver: sqlite name: myapp.db

compose.yaml and compose.production.yaml

A db service is emitted with the right image, ports, environment, and healthcheck. SQLite omits the service entirely (the database is a file on disk; nothing to run in a container). The app service’s depends_on: db is removed in the SQLite case.

.env.example

Per-driver DATABASE_* defaults. Connection strings point at localhost and the driver’s default port; the SQLite case carries only DATABASE_NAME.

deployments/docker/dev.dockerfile

The startup command builds the right migrate URL shape per driver:

  • Postgres: postgres://user:pass@host:port/db?sslmode=disable
  • MySQL: mysql://user:pass@tcp(host:port)/db
  • SQLite: sqlite3://./<project>.db
  • SQL Server: sqlserver://user:pass@host:port?database=db
  • ClickHouse: clickhouse://user:pass@host:port/db

The migrate binary is installed with all five driver tags regardless of --driver, so switching DBs in the same generated project is just a config change away (though the migration files won’t move with you — see the caveat below).

db/migrations/

This is the biggest difference. Each driver gets a hand-written foundational migration tree under internal/skeleton/migrations/<driver>/, copied into the new project at scaffold time:

  • Postgres: 5 migrations — citext extension, three shared PL/pgSQL functions (update_updated_at_column, avoid_deleting_not_deletable_record, increment_record_version), and users. Subsequent g scaffold migrations reuse the shared functions via per-table triggers.
  • MySQL: 1 migration — users with the triggers inlined into the same file (MySQL has no equivalent of CREATE OR REPLACE FUNCTION for the gofasta pattern).
  • SQLite: 1 migration — users with per-table AFTER UPDATE and BEFORE DELETE WHEN triggers inlined.
  • SQL Server: 1 migration — users with a combined AFTER UPDATE trigger (covers updated_at + record_version++) and a separate INSTEAD OF DELETE trigger for the not-deletable guard.
  • ClickHouse: 1 migration — users only, with a comment explaining that triggers don’t apply.

README.md

Driver-specific quickstart commands (docker compose up db -d skipped for SQLite, native client install hints per driver).

Examples

Postgres (default)

gofasta new myapp cd myapp gofasta init gofasta dev --services db # db in Docker, app on the host — migrations run automatically

MySQL

gofasta new myapp --driver mysql cd myapp gofasta init gofasta dev --services db

SQLite (no Compose service)

gofasta new myapp --driver sqlite cd myapp gofasta init gofasta dev # creates myapp.db in the project root on first migrate

SQL Server

gofasta new myapp --driver sqlserver cd myapp gofasta init gofasta dev --services db

ClickHouse

gofasta new myapp --driver clickhouse cd myapp gofasta init gofasta dev --services db

Verifying DB-level enforcement

Once gofasta dev has started (it applies pending migrations automatically — or run gofasta migrate up yourself), you can confirm the triggers fire by talking to the database directly (i.e. bypassing the generated repositories). Example for Postgres:

# Insert a non-deletable row, then try to delete it via psql: docker compose exec db psql -U myapp -c "UPDATE users SET is_deletable = false WHERE email = 'admin@example.com';" docker compose exec db psql -U myapp -c "DELETE FROM users WHERE email = 'admin@example.com';" # → ERROR: This record is not deletable

Replace psql with the native client for your driver (mysql, sqlite3, sqlcmd, clickhouse-client). For ClickHouse, the DELETE will succeed at the DB layer — the not-deletable check lives in the repository instead.

Caveats

  • Driver choice is sticky. The generated db/migrations/*.sql files use driver-specific syntax. Switching database.driver in config.yaml after scaffold won’t rewrite them; you’d have to regenerate or hand-port. Pick the driver you want at gofasta new time.
  • ClickHouse is intentionally an analytics target. It’s wired up for completeness, but the typical use case is OLAP — write-heavy transactional workloads work better on one of the other four.
  • g scaffold <Resource> already branches on the driver. Once the project is generated with --driver mysql, every subsequent gofasta g scaffold or gofasta g migration emits MySQL syntax automatically by reading database.driver from config.yaml. No flag needed.
Last updated on