Database Connections
Database connections are the source-of-truth path for durable relational data in a GoForj App.
GoForj keeps database configuration explicit. The Framework-managed internal/database connection registry opens and caches connections on first access.
Open the Default Connection
Database-enabled Apps expose the configured default connection through:
forj dbExpected result: GoForj resolves the default DB_* configuration and opens the matching mysql, psql, or sqlite3 shell. It tries the local client first and falls back to the matching generated Docker Compose service only when the local client is missing and that service exists.
Use --print to verify the masked command without opening a shell:
forj db --printApplication code reaches the same default connection through the generated registry:
db, err := conns.Default()The connection opens on first access and is then cached by name.
Database Package
Database connection behavior lives in:
internal/databaseThe database package owns:
- database connection configuration
- first-access connection opening
- default and named connection access
- driver-specific support produced during the build
- local database README guidance
Default Connection
The default connection uses DB_* variables:
DB_SUPPORTED_DRIVERS=sqlite,postgres
DB_DRIVER=sqlite
DB_DATABASE=./_data/sqlite/app.dbIf DB_DRIVER is unset, GoForj Apps use SQLite. If a SQLite connection does not set DB_DATABASE, the default connection uses _data/sqlite/app.db.
For networked databases, the App can use host, database, username, password, port, pool, and query logging settings.
Named Connections
Named connections use DB_<NAME>_* variables:
DB_ANALYTICS_DRIVER=postgres
DB_ANALYTICS_HOST=127.0.0.1
DB_ANALYTICS_DATABASE=analytics
DB_ANALYTICS_USERNAME=app
DB_ANALYTICS_PASSWORD=secretIf a named connection uses the SQLite fallback and no database path is configured, it uses _data/sqlite/<name>.db, such as _data/sqlite/analytics.db.
After changing named connections or supported drivers, use the normal build path:
forj buildDuring forj dev, an app listed in dev.apps rebuilds automatically. Generation Commands covers focused maintainer workflows.
Accessing Connections
Generated accessors expose default and named connections:
db, err := conns.Default()
analytics, err := conns.Analytics()Connections are opened on first accessor use and cached by name. This database-specific behavior does not imply that every generated manager uses lazy initialization.
Use health and readiness checks to make required database availability visible for the runtime process that needs it.
Shell Options
Use the canonical command when you want the full name:
forj db:shellAdditional apps use the app-name prefix during source-aware development:
forj admin dbWhen validating a built artifact directly, use its binary:
./bin/app db
./bin/admin dbNamed connections use the App-facing connection name:
forj db analytics
forj db --connection analyticsConnection selectors match generated resource names: DB_ANALYTICS_* maps to analytics. With multiple shellable connections, an interactive terminal shows a compact selector. A non-interactive command uses the default connection unless you pass a name; scripts should select one explicitly when they must not depend on that default.
Method Selection
By default, forj db tries the local client first. It falls back to the generated Docker Compose service only when that client is missing and the matching service exists. Other local resolution errors are returned directly; if neither launch method is available, the error identifies the missing client and unavailable Compose fallback.
You can choose the method explicitly:
forj db --method local
forj db --method composeUse --print to inspect the command GoForj will run. Secrets are masked:
forj db --print
forj db analytics --method local --printThe printed command is useful for checking the selected client, host, port, database, connection arguments, and method without launching it.
Non-Interactive Queries
Use --exec for a single SQL string:
forj db --exec "select count(*) from users"
forj db analytics --exec "select count(*) from events"Use -- to pass client-native arguments directly after GoForj adds the configured connection arguments:
forj db -- --batch -e "select count(*) from users"
forj db analytics -- -c "select count(*) from events"
forj db --connection analytics -- -c "select now()"The first example passes MySQL-style arguments. The analytics examples pass Postgres-style arguments when that connection uses Postgres.
Local MySQL launches force TCP when the connection is host-based, preventing the client from silently choosing a local socket.
Driver Support
DB_SUPPORTED_DRIVERS controls which database drivers are generated into the App. DB_DRIVER and DB_<NAME>_DRIVER choose active runtime connections.
Example:
DB_SUPPORTED_DRIVERS=sqlite,postgres
DB_DRIVER=sqlite
DB_ANALYTICS_DRIVER=postgresThis compiles SQLite and Postgres support, uses SQLite for the default connection, and uses Postgres for analytics.
Verify a Release
Test repository behavior against the same database engine used in production when SQL dialect, locking, or transaction semantics matter. Before routing traffic to a built artifact, verify the selected connection without exposing its password:
./bin/app db --print
./bin/app db --exec "select 1"Expected result: the printed command names the intended client, host, port, and database with secrets masked, and the query exits successfully with one row. Repeat with --connection <name> for every connection the release uses.
After the HTTP Runtime starts, readiness is the operational handoff for required database connections:
./bin/app health --probe ready --failA non-zero exit means the instance should remain out of traffic while the failed connection, migration, credentials, or network path is repaired.
Source of Truth
Use the database for durable business state.
Do not use cache as the source of truth. Do not use object storage as a replacement for relational state unless the data is actually file/blob data.
Next Steps
- Migrations explains schema changes.
- Repositories explains persistence boundaries.
- Driver Selection explains local and production driver choices.
- Environment Reference lists connection and driver settings.