Configuration — the full surface
The Configuration page in getting-started covers the fast-kickoff: minimum config + connectToDatabase. This page picks up where that ends. It covers every option ConnectionOptions accepts, the model-defaults hierarchy, multi-data-source registration, and the migration-level knobs.
If you’ve gotten this far through the docs, you’ve already configured a database. This page is for when you need to tune — performance pools, custom column conventions, multi-tenant routing, migration table naming, the works.
The shape of connectToDatabase
Section titled “The shape of connectToDatabase”import { connectToDatabase } from "@warlock.js/cascade";
const dataSource = await connectToDatabase({ driver: "postgres", database: "myapp", // ... everything else is optional});connectToDatabase returns a DataSource instance and registers it with the global dataSourceRegistry. Most apps call it once at boot and never touch the return value — models reach their data source through the registry.
The full option surface organizes into four buckets, each with its own concern:
| Bucket | What it configures | Lives on |
|---|---|---|
| Shared | driver, connection details, logging | top-level fields |
driverOptions | Cascade-side driver knobs (id generation, sessions, …) | driverOptions: {...} |
clientOptions | The native database client (mongodb / pg packages) | clientOptions: {...} |
modelOptions | Default model behaviour for every model on this data source | modelOptions: {...} |
The split is intentional: shared config is portable across drivers, clientOptions punches through to the native library when you need its specific knob, and modelOptions is where you set cross-cutting model behaviour without touching each model.
Shared connection config
Section titled “Shared connection config”const ds = await connectToDatabase({ // Identity driver: "postgres", // "mongodb" | "postgres" | "mysql" (coming) name: "primary", // unique identifier in the registry (default: "default") isDefault: true, // mark as the default data source (default: true)
// What database database: "myapp", // required
// Where it lives uri: "postgres://...", // OR host: "localhost", port: 5432, username: "myapp", password: process.env.DB_PASSWORD, authSource: "admin", // MongoDB-specific
// Operations logging: false, // log every query? Leave OFF in production.});A few of these earn their own callouts:
name and isDefault
Section titled “name and isDefault”The registry can hold multiple data sources, keyed by name. Without a name, you get "default". The first call to connectToDatabase becomes the default; subsequent calls override unless you pass isDefault: false:
await connectToDatabase({ name: "primary", database: "main", isDefault: true });await connectToDatabase({ name: "analytics", database: "warehouse", isDefault: false });Per-model routing (static dataSource = "analytics") and the multi-database story are covered in the Multi-database guide.
uri vs host/port
Section titled “uri vs host/port”Pick one. URIs are convenient for .env-driven config; the split form is convenient when host/port come from different secrets:
// URI formconnectToDatabase({ driver: "postgres", database: "myapp", uri: process.env.DATABASE_URL });
// Split formconnectToDatabase({ driver: "postgres", database: "myapp", host: process.env.DB_HOST, port: 5432, username: process.env.DB_USER, password: process.env.DB_PASSWORD,});Both work on both drivers.
logging
Section titled “logging”connectToDatabase({ logging: true });Logs every query, its bindings, and execution time. Useful in development; disabled by default and recommended OFF in production — query logs can contain sensitive data and saturate your log shipper at scale.
Driver-specific options — driverOptions
Section titled “Driver-specific options — driverOptions”Cascade-level driver knobs go here. The shape depends on the driver.
MongoDB driver options
Section titled “MongoDB driver options”import type { MongoDriverOptions } from "@warlock.js/cascade";
await connectToDatabase<MongoDriverOptions>({ driver: "mongodb", database: "myapp", driverOptions: { autoGenerateId: true, // assign numeric ids via counters counterCollection: "counters", // where the counters live transactionOptions: { // defaults for transactions readPreference: "primary", readConcern: { level: "majority" }, writeConcern: { w: "majority" }, }, },});Postgres driver options
Section titled “Postgres driver options”Postgres has fewer Cascade-side knobs today — most options flow through clientOptions (which maps to pg’s PoolConfig). Schema selection lives here when it matters.
Native client options — clientOptions
Section titled “Native client options — clientOptions”clientOptions punches through to the native database client library:
- MongoDB —
MongoClientOptionsfrom themongodbpackage (pool sizes, timeouts, SSL, replica set config, …) - Postgres —
PoolConfigfrom thepgpackage (pool sizes, statement timeouts, SSL, …)
import type { MongoClientOptions } from "mongodb";import type { MongoDriverOptions } from "@warlock.js/cascade";
await connectToDatabase<MongoDriverOptions, MongoClientOptions>({ driver: "mongodb", database: "myapp", clientOptions: { minPoolSize: 5, maxPoolSize: 50, serverSelectionTimeoutMS: 5_000, retryWrites: true, },});await connectToDatabase({ driver: "postgres", database: "myapp", clientOptions: { max: 20, // max connections in the pool idleTimeoutMillis: 30_000, statement_timeout: 10_000, // SQL-level statement timeout (ms) ssl: { rejectUnauthorized: true }, },});When in doubt about which option is Cascade’s vs the native library’s: Cascade-side options are documented in the MongoDriverOptions / Postgres types; everything else is the native library. The split keeps the API portable — same Cascade options on every driver, native escape hatch when you specifically need it.
Model defaults — modelOptions
Section titled “Model defaults — modelOptions”modelOptions sets behaviour for every model on this data source unless the individual model overrides. The hierarchy, highest priority first:
- Model static property —
User.deletedAtColumn = "archived_at" modelOptions— database-wide override- Driver default — Postgres: snake_case, MongoDB: camelCase
- Framework default — fallback values
await connectToDatabase({ driver: "postgres", database: "myapp", modelOptions: { // Override Postgres default (snake_case) — use camelCase namingConvention: "camelCase",
// Timestamp columns createdAtColumn: "createdAt", updatedAtColumn: "updatedAt", deletedAtColumn: "deletedAt", timestamps: true,
// ID generation (mostly MongoDB-relevant) autoGenerateId: false, randomIncrement: true, initialId: 1000,
// Default delete strategy for every model deleteStrategy: "soft", trashTable: "archive", // shared trash table for all models },});Pick the keys you actually need to override — leave the rest to the driver defaults.
A few of these worth a closer look:
namingConvention
Section titled “namingConvention”The default differs per driver: Postgres treats columns as snake_case, MongoDB treats them as camelCase. The convention controls how Cascade reads/writes things like createdAt vs created_at. Override when your schema diverges from the driver convention.
timestamps
Section titled “timestamps”When true, Cascade auto-stamps createdAtColumn and updatedAtColumn on insert/update. Default: true. Disable per model with static timestamps = false or globally here.
autoGenerateId (MongoDB)
Section titled “autoGenerateId (MongoDB)”MongoDB doesn’t ship with auto-increment ids. When autoGenerateId: true, Cascade uses a counter collection to issue sequential numeric ids. Combine with randomIncrement and initialId for non-predictable id ranges. For Postgres, the database handles this natively — leave the option off.
deleteStrategy / trashTable
Section titled “deleteStrategy / trashTable”The data-source-wide defaults for the delete strategy precedence chain (see Delete strategies guide). Set once here to make every model on this data source use soft-delete (or trash) by default; override per model when needed.
Migration defaults — migrationOptions
Section titled “Migration defaults — migrationOptions”UUID strategy and other migration-time defaults:
await connectToDatabase({ driver: "postgres", database: "myapp", migrationOptions: { uuidStrategy: "v7", // default UUID version for new migrations },});These override driver migration defaults but can be overridden by individual migration calls.
Migration runtime config — migrations
Section titled “Migration runtime config — migrations”The migrations object configures the migration runner itself:
await connectToDatabase({ driver: "postgres", database: "myapp", migrations: { transactional: true, // wrap each migration in a transaction table: "_migrations", // tracking table name },});transactional— Postgres default istrue(DDL is transactional in PG); MongoDB default isfalse(Mongo DDL can’t run in a transaction). Override per migration with the migration’sstatic transactionalproperty.table— the bookkeeping table name. Default"_migrations". Change if your project already uses that name for something else.
Data-source-wide delete defaults
Section titled “Data-source-wide delete defaults”Two separate keys exist at the top level of ConnectionOptions for the soft-delete story:
await connectToDatabase({ defaultDeleteStrategy: "soft", // or "trash" / "permanent" defaultTrashTable: "RecycleBin",});These are the fallbacks in the strategy resolution chain (see Delete strategies guide) — they apply when neither the per-call options nor the per-model static set anything. The modelOptions.deleteStrategy / modelOptions.trashTable keys cover the same ground; either works.
Multiple data sources
Section titled “Multiple data sources”Register several:
await connectToDatabase({ name: "primary", driver: "postgres", database: "main",});
await connectToDatabase({ name: "analytics", driver: "postgres", database: "warehouse", isDefault: false,});Models default to the registry’s default data source. Override per model:
@RegisterModel()export class AnalyticsEvent extends Model<EventSchema> { public static table = "events"; public static schema = eventSchema; public static dataSource = "analytics";}See the Multi-database guide for the full multi-data-source story (cross-source joins, transaction limits, separate schemas).
Reaching the driver directly
Section titled “Reaching the driver directly”For diagnostic queries, custom DDL, or driver-specific features outside the structured API:
import { getDatabaseDriver, transaction } from "@warlock.js/cascade";
const driver = getDatabaseDriver();// driver is typed as DriverContract; cast to a specific driver type when neededgetDatabaseDriver<PostgresDriver>() accepts a type parameter when you want the concrete driver’s full API surface (e.g., Postgres-specific advisory locks).
A complete example — production Postgres config
Section titled “A complete example — production Postgres config”import { connectToDatabase } from "@warlock.js/cascade";
await connectToDatabase({ driver: "postgres", name: "primary", database: "myapp",
uri: process.env.DATABASE_URL,
clientOptions: { max: 20, idleTimeoutMillis: 30_000, statement_timeout: 10_000, ssl: { rejectUnauthorized: true }, },
modelOptions: { namingConvention: "snake_case", timestamps: true, deleteStrategy: "soft", },
migrationOptions: { uuidStrategy: "v7", },
migrations: { transactional: true, table: "_migrations", },
defaultDeleteStrategy: "soft", logging: false, // OFF in production});That’s roughly what a real production config looks like — 20 connections, hard 10-second statement timeout, SSL on, soft-delete throughout, UUIDv7 for new tables, transactional migrations, no query logging. Adjust the numbers for your scale; the shape is the same.
Going further
Section titled “Going further”- Per-model data-source routing and multi-tenant patterns: Multi-database guide
- Delete strategy resolution and trash-table semantics: Delete strategies guide
- The Operations API (programmatic migration runs for tooling, CI, embedded use): Operations API reference; standalone binary: CLI guide
- Transactions (the
transaction()utility, Mongo replica-set requirement): Transactions guide