Database Drivers — PostgreSQL, MySQL, SQLite

Connect SysMARA ORM to PostgreSQL, MySQL, or SQLite through a unified driver interface.

Overview

Starting with v0.7.0, SysMARA ORM supports real database drivers for PostgreSQL, MySQL, and SQLite. Each driver is an optional peer dependency — you install only the one you need. When no driver is installed, the ORM falls back to an in-memory store suitable for testing and local development.

All drivers implement a unified DatabaseDriver interface, so switching between databases is a configuration change, not a code change. The query builder produces PostgreSQL-style $1, $2 placeholders. MySQL and SQLite drivers convert placeholders and handle quoting differences automatically.

Supported Drivers

Driver npm Package Provider Notes
pg pg postgresql Connection pooling via pg.Pool. Native $1, $2 placeholders.
mysql2 mysql2 mysql Promise-based pool. Converts $N to ? and double-quotes to backticks.
better-sqlite3 better-sqlite3 sqlite Synchronous driver wrapped in async interface. WAL mode enabled by default. Handles RETURNING emulation.
In-Memory None (built-in) Any JavaScript Map-based store. No installation required. Used as fallback.

Installation

Install the driver for your target database as a peer dependency:

# PostgreSQL
npm install pg
npm install -D @types/pg

# MySQL
npm install mysql2

# SQLite
npm install better-sqlite3
npm install -D @types/better-sqlite3

Only install the driver you need. SysMARA dynamically imports the package at runtime, so uninstalled drivers do not cause errors until you attempt to connect with that provider.

Configuration

Set the provider and connectionString in your sysmara.config.yaml:

# sysmara.config.yaml
database:
  adapter: sysmara-orm
  provider: postgresql      # postgresql | mysql | sqlite
  connectionString: env(DATABASE_URL)

Example connection strings for each provider:

# PostgreSQL
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb

# MySQL
DATABASE_URL=mysql://user:pass@localhost:3306/mydb

# SQLite (file path or in-memory)
DATABASE_URL=sqlite://./data/app.db
DATABASE_URL=:memory:

How It Works

The DatabaseDriver Interface

Every driver implements a unified four-method interface:

interface DatabaseDriver {
  readonly provider: DatabaseProvider;
  connect(): Promise<void>;
  disconnect(): Promise<void>;
  query(sql: string, params?: unknown[]): Promise<QueryResult>;
  exec(sql: string): Promise<void>;
}
Method Description
connect() Opens a connection (or pool) to the database. Tests connectivity before resolving.
disconnect() Closes the connection and releases resources.
query(sql, params) Executes a parameterized SQL query. Returns { rows, rowCount }.
exec(sql) Executes raw SQL statements (e.g. CREATE TABLE). Used for schema application.

Placeholder Conversion

The query builder always produces PostgreSQL-style $1, $2, $3 placeholders. This is the canonical format across SysMARA ORM. When you use the MySQL or SQLite driver, the driver transparently converts:

  • $1, $2 placeholders become ? placeholders.
  • Parameters are reordered to match the $N indices (PostgreSQL allows out-of-order references like $3, $1, $2).
  • MySQL converts double-quoted identifiers to backtick-quoted identifiers ("user" becomes `user`).
  • SQLite strips PostgreSQL-specific statements like CREATE EXTENSION and emulates RETURNING * with a follow-up SELECT.

Driver Factory

Drivers are created through two factory functions:

import { createDriver, createInMemoryDriver } from 'sysmara/database/adapters/sysmara-orm/driver';

// Create a real driver based on provider
const driver = createDriver('postgresql', process.env.DATABASE_URL);

// Create an in-memory driver for testing
const memDriver = createInMemoryDriver();

In-Memory Fallback

When no database driver package is installed, SysmaraORM automatically uses the built-in InMemoryDriver. This driver stores data in plain JavaScript Map objects and supports the same query interface as the real drivers.

The in-memory driver is useful for:

  • Unit testing — no database setup required.
  • Local development — prototype quickly without running a database server.
  • CI pipelines — run integration tests without external dependencies.

The in-memory driver parses CREATE TABLE statements from exec() calls to initialize table storage, and handles SELECT, INSERT, UPDATE, and DELETE operations with basic WHERE clause parsing. It auto-generates IDs and timestamps for inserted rows.

Repository Integration

In most cases, you do not interact with the driver directly. The driver is used internally by SysmaraRepository, which provides capability-scoped, typed CRUD operations. The ORM selects and manages the driver based on your configuration.

Full Example

import { SysmaraORM } from 'sysmara/database/adapters/sysmara-orm/orm';

// 1. Create the ORM with your config and parsed specs
const orm = new SysmaraORM(
  { adapter: 'sysmara-orm', provider: 'postgresql', connectionString: process.env.DATABASE_URL },
  specs
);

// 2. Connect — creates a PostgresDriver internally
await orm.connect();

// 3. Apply schema — runs CREATE TABLE statements through the driver
await orm.applySchema();

// 4. Use a typed repository for CRUD
const userRepo = orm.repository<User>('user', 'create_user');

// Insert
const newUser = await userRepo.create({ email: 'alice@example.com', role: 'admin' });

// Query
const found = await userRepo.findOne({ email: 'alice@example.com' });

// Update
const updated = await userRepo.update(newUser.id, { role: 'member' });

// Delete
await userRepo.delete(newUser.id);

// 5. Disconnect when done
await orm.disconnect();

The same code works across all providers. Change provider to 'mysql' or 'sqlite' and install the corresponding package — no application code changes required.

Related Concepts

  • SysMARA ORM — the AI-first ORM that uses these drivers
  • Database Adapters — the pluggable adapter system for Prisma, Drizzle, TypeORM, and SysMARA ORM
  • Entities — YAML entity specs that define the database schema
  • CLI Referencesysmara db commands