ObjectStackObjectStack

Database Drivers

Configuration reference for supported database drivers

Database Drivers

ObjectStack supports multiple database backends through a unified driver interface. Drivers can be selected in two ways:

  1. Env var only — auto-inferred from the URL scheme (the simplest path):

    export OS_DATABASE_URL=mongodb://localhost:27017/myapp   # → MongoDB
    export OS_DATABASE_URL=postgres://user:pass@host/db      # → PostgreSQL
    export OS_DATABASE_URL=mysql://user:pass@host/db         # → MySQL
    export OS_DATABASE_URL=file:./data/app.db                # → SQLite
    pnpm dev

    The CLI inspects the connection-string scheme and picks the right driver automatically. You only need to set OS_DATABASE_DRIVER when you want to override the inference (e.g. force sqlite for an ambiguous path).

  2. Programmatic — register a driver instance as a kernel plugin via DriverPlugin (the same path the CLI uses internally):

    import { DriverPlugin } from '@objectstack/runtime';
    import { SqlDriver } from '@objectstack/driver-sql';
    
    await kernel.use(
      new DriverPlugin(
        new SqlDriver({
          client: 'pg',
          connection: process.env.OS_DATABASE_URL,
        }),
      ),
    );

URL → Driver Inference Table

URL patternInferred drivernpm package
mongodb://…, mongodb+srv://…MongoDB@objectstack/driver-mongodb
postgres://…, postgresql://…PostgreSQL (Knex pg)@objectstack/driver-sql + pg
mysql://…, mysql2://…MySQL (Knex mysql2)@objectstack/driver-sql + mysql2
wasm-sqlite://…, *.wasm.dbSQLite (pure-JS WASM)@objectstack/driver-sqlite-wasm
file:…, sqlite:…, :memory:, *.db / *.sqliteSQLite (Knex better-sqlite3)@objectstack/driver-sql + better-sqlite3
(unset, dev mode)SQLite (native, falling back to WASM, then in-memory)@objectstack/driver-sql / -sqlite-wasm / -memory

Turso / libSQL (libsql://, *.turso.io) is not supported by the open-source framework — no bundled driver dispatches libsql:// URLs.

Supported Drivers

Drivernpm packageClassExplicit OS_DATABASE_DRIVER value
PostgreSQL@objectstack/driver-sql (peer: pg)SqlDriverpostgres | postgresql | pg
MySQL@objectstack/driver-sql (peer: mysql2)SqlDrivermysql | mysql2
SQLite@objectstack/driver-sql (peer: better-sqlite3)SqlDriversqlite | sql
SQLite (WASM)@objectstack/driver-sqlite-wasmSqliteWasmDriversqlite-wasm | wasm-sqlite | wasm
MongoDB@objectstack/driver-mongodbMongoDBDrivermongodb | mongo
Memory@objectstack/driver-memoryInMemoryDrivermemory

All SQL flavours (PostgreSQL / MySQL / SQLite) are served by a single @objectstack/driver-sql package — there is no separate driver-postgres, driver-mysql, or driver-sqlite package. Pick the Knex client name (pg / mysql2 / better-sqlite3) when you instantiate SqlDriver.

PostgreSQL (via @objectstack/driver-sql)

pnpm add @objectstack/driver-sql pg

SqlDriver's constructor accepts a Knex Knex.Config object verbatim.

import { SqlDriver } from '@objectstack/driver-sql';

new SqlDriver({
  client: 'pg',
  connection: {
    host: 'db.example.com',
    port: 5432,
    user: 'admin',
    password: process.env.DB_PASSWORD,
    database: 'myapp',
    ssl: true,
  },
  pool: { min: 2, max: 10 },
});

Or via env var alone (driver inferred from postgres:// scheme):

export OS_DATABASE_URL=postgres://admin:secret@db.example.com:5432/myapp

MongoDB

Configuration properties for the MongoDB driver.

PropertyTypeRequiredDescription
urlstringConnection URI (e.g., mongodb://host:27017/db)
databasestringoptionalDatabase name (overrides the database in the URI)
maxPoolSizenumberoptionalMax connection pool size (default: 10)
minPoolSizenumberoptionalMin connection pool size (default: 1)
connectTimeoutMSnumberoptionalConnection timeout in milliseconds
serverSelectionTimeoutMSnumberoptionalServer selection timeout in milliseconds
optionsMongoClientOptionsoptionalAdditional MongoClient options (e.g. ssl, replicaSet, readPreference, authSource)

Example (config):

import { MongoDBDriver } from '@objectstack/driver-mongodb';

new MongoDBDriver({
  url: 'mongodb+srv://admin:secret@cluster.mongodb.net/myapp',
  maxPoolSize: 50,
  options: { readPreference: 'nearest' },
});

Example (env-var, recommended for objectstack dev / objectstack serve):

export OS_DATABASE_URL=mongodb://localhost:27017/myapp
pnpm dev

The CLI infers the MongoDB driver from the mongodb:// (or mongodb+srv://) URL scheme automatically — no OS_DATABASE_DRIVER needed. Set OS_DATABASE_DRIVER=mongodb only if you want to be explicit.

SQLite (via @objectstack/driver-sql)

SQLite is ideal for local-first development and embedded applications. It uses the same SqlDriver class with client: 'better-sqlite3'.

pnpm add @objectstack/driver-sql better-sqlite3
import { SqlDriver } from '@objectstack/driver-sql';

new SqlDriver({
  client: 'better-sqlite3',
  connection: { filename: './data/app.db' },
  useNullAsDefault: true,
});

Env-var path (driver inferred from file: / :memory: / .db paths):

export OS_DATABASE_URL=file:./data/app.db
# or
export OS_DATABASE_URL=:memory:

Space Reclamation & Table Rotation (ADR-0057)

The SQL driver connects SQLite with auto_vacuum=INCREMENTAL and exposes reclaimSpace() (PRAGMA incremental_vacuum), which the platform LifecycleService calls after every sweep that deleted rows — so the database file actually shrinks instead of pinning at its high-water mark. Objects declaring lifecycle.storage.strategy: 'rotation' are physically time-sharded on SQLite: writes land in the current shard, reads go through a view under the table's name, and expired shards are reclaimed with a single DROP TABLE. On PostgreSQL/MySQL both are no-ops — those engines manage space with their own vacuum machinery, and rotation falls back to an equivalent age-based reap.

Memory Driver

The in-memory driver keeps records in plain in-process objects (queried via mingo). Data is lost when the process exits. It is the last-resort fallback in dev mode: objectstack dev prefers native SQLite (better-sqlite3), falls back to the pure-JS WASM SQLite driver if the native binary is unavailable, and only drops to the in-memory driver if WASM also fails to load. Set OS_DATABASE_DRIVER=memory to select it explicitly.

import { InMemoryDriver } from '@objectstack/driver-memory';

new InMemoryDriver();

Use the memory driver for unit tests. It requires no setup and runs instantly.

Local Environment Runtime

Local framework commands run one active environment. The business records served at /api/v1/data/* use the driver selected by OS_DATABASE_URL and OS_DATABASE_DRIVER.

# Run the showcase with MongoDB as the local environment DB:
OS_ENVIRONMENT_ID=env_local \
OS_DATABASE_URL=mongodb://localhost:27017/objectstack \
pnpm dev

# Pin the driver explicitly when the URL is ambiguous:
OS_DATABASE_URL=file:./.objectstack/data/app.db \
OS_DATABASE_DRIVER=sqlite \
pnpm dev

Cloud control-plane databases and environment provisioning live in the Cloud distribution outside this framework repo. When this runtime is pointed at Cloud, the environment id still flows through OS_ENVIRONMENT_ID, scoped URLs, or X-Environment-Id; mutable deployment config does not live in the compiled artifact.

Multi-Datasource

ObjectStack supports multiple data sources. Objects can target specific datasources:

Datasources are declared as metadata descriptors ({ driver, config }) — the map key becomes the datasource name:

import { defineStack } from '@objectstack/spec';

export default defineStack({
  datasources: {
    analytics: {
      driver: 'postgres',
      config: { url: process.env.ANALYTICS_URL },
    },
    cache: {
      driver: 'mongodb',
      config: { url: process.env.MONGO_URL },
    },
  },
});

Then in your object definition:

export const AuditLog = ObjectSchema.create({
  name: 'audit_log',
  datasource: 'analytics', // Routes to the analytics database
  fields: { /* ... */ },
});

On this page