db Stable WINMACLNX
Built-in SQLite 3 with WAL mode enabled by default. All calls are async (return Promise).
Requires capability: "db": true in glyx.config.json.
Import
import { db } from '@glyx-dev/react'Opening a database
// Open once at startup — auto-sets the default handle.
await db.open('app.db')
// Multi-DB: keep explicit handles.
const usersDb = await db.open('users.db')
const logsDb = await db.open('logs.db')
db.setDefault(logsDb)":memory:" opens an in-process ephemeral database. Paths are resolved relative to the app's data directory.
Queries
db.query(sql, params?)
SELECT and return all rows as plain objects:
const notes = await db.query('SELECT * FROM notes ORDER BY created_at DESC')
// → { id: number, title: string, body: string }[]
const note = await db.query('SELECT * FROM notes WHERE id = ?', [id])
// → use note[0] for a single rowdb.run(sql, params?)
INSERT, UPDATE, DELETE, or DDL:
await db.run(`
CREATE TABLE IF NOT EXISTS notes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
body TEXT,
created_at INTEGER DEFAULT (unixepoch())
)
`)
const { lastInsertId, rowsAffected } = await db.run(
'INSERT INTO notes (title, body) VALUES (?, ?)',
['My note', 'Content here']
)
console.log(lastInsertId) // → 1
console.log(rowsAffected) // → 1db.transaction(stmts)
Run multiple statements atomically. Any failure rolls back all:
await db.transaction([
{ sql: 'INSERT INTO archived SELECT * FROM notes WHERE id = ?', params: [id] },
{ sql: 'DELETE FROM notes WHERE id = ?', params: [id] },
])All methods accept an optional explicit handle as the first argument when working with multiple databases:
await db.run(logsDb, 'INSERT INTO events (msg) VALUES (?)', ['started'])
await db.query(usersDb, 'SELECT * FROM users')
await db.transaction(logsDb, [{ sql: 'DELETE FROM events WHERE ttl < ?', params: [Date.now()] }])Migrations
db.migrate() tracks applied versions in _glyx_migrations and only runs pending entries.
Each migration is committed atomically with its tracking record — a partial failure leaves the database clean.
await db.migrate([
{
version: 1,
name: 'create_notes',
up: `CREATE TABLE notes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL DEFAULT '',
body TEXT NOT NULL DEFAULT '',
created_at INTEGER NOT NULL DEFAULT (unixepoch())
)`,
},
{
version: 2,
name: 'add_pinned',
up: `ALTER TABLE notes ADD COLUMN pinned INTEGER NOT NULL DEFAULT 0`,
},
{
version: 3,
name: 'add_fts',
// Multiple statements — pass an array:
up: [
`CREATE VIRTUAL TABLE notes_fts USING fts5(title, body, content=notes, content_rowid=id)`,
`CREATE TRIGGER notes_ai AFTER INSERT ON notes BEGIN
INSERT INTO notes_fts(rowid, title, body) VALUES (new.id, new.title, new.body);
END`,
],
},
])up is a string (single statement) or an array of strings. Migrations run in ascending version order.
Seeding
db.seed() runs setup data after migrations:
// Always run — write idempotent SQL:
await db.seed(async () => {
await db.run('INSERT OR IGNORE INTO settings (key, value) VALUES (?, ?)', ['theme', 'dark'])
})
// Run once per name — tracked in _glyx_seeds:
await db.seed('sample_notes', async () => {
await db.run('INSERT INTO notes (title) VALUES (?)', ['Welcome'])
})Backups
db.backup(destPath)
Create an atomic online backup using SQLite's VACUUM INTO. Works with WAL mode and
does not block reads or writes on the source database.
await db.backup('./backups/app-manual.sqlite')
// With explicit handle:
await db.backup(usersDb, './backups/users-2026-07-09.sqlite')The destination directory is created automatically. Any existing file at destPath
is overwritten atomically.
db.config({ backup })
Schedule automatic backups on a timer:
db.config({
backup: {
dir: './backups', // destination directory (relative to app data dir, or absolute)
interval: '1h', // '1h' | '6h' | '12h' | '24h' | 'daily' | milliseconds
keep: 5, // number of backup files to retain (oldest pruned)
}
})Backup files are named backup-<ISO8601>.sqlite (e.g. backup-2026-07-09T14-00-00.sqlite).
Old files beyond keep are pruned automatically.
Calling db.config() again with the same handle replaces the previous schedule.
Omit backup to cancel:
db.config({}) // cancels auto-backup for the default handle| Option | Type | Default | Description |
|---|---|---|---|
dir | string | './backups' | Backup directory |
interval | string | number | '24h' | Schedule interval |
keep | number | 5 | Max backup files to retain |
Using Drizzle ORM
@glyx-dev/drizzle bridges Drizzle ORM to the native Glyx SQLite bindings via the
sqlite-proxy driver — no better-sqlite3 or any other native dependency needed.
Full Drizzle docs at packages/drizzle.
npm install drizzle-orm @glyx-dev/drizzleDefine your schema:
import { sqliteTable, integer, text, sql } from 'drizzle-orm/sqlite-core'
export const notes = sqliteTable('notes', {
id: integer('id').primaryKey({ autoIncrement: true }),
title: text('title').notNull(),
body: text('body'),
createdAt: integer('created_at').default(sql`(unixepoch())`),
})Create the Drizzle instance from a Glyx db handle:
import { db } from '@glyx-dev/react'
import { createDrizzle } from '@glyx-dev/drizzle'
import * as schema from './schema'
const handle = await db.open('app.db')
export const drizzleDb = createDrizzle(handle, schema)Use it anywhere:
import { drizzleDb } from './db'
import { notes } from './schema'
import { desc, eq } from 'drizzle-orm'
const allNotes = await drizzleDb.select().from(notes).orderBy(desc(notes.createdAt))
const note = await drizzleDb.select().from(notes).where(eq(notes.id, 1))
await drizzleDb.insert(notes).values({ title: 'New note', body: '' })
await drizzleDb.delete(notes).where(eq(notes.id, id))createDrizzle accepts an optional Drizzle relational schema as the second argument to enable the .query.* relational API.
Database location
Paths passed to db.open() are resolved relative to the app's data directory:
macOS: ~/Library/Application Support/<app-name>/
Windows: %APPDATA%\<app-name>\
Linux: ~/.local/share/<app-name>/Use an absolute path to override:
await db.open('/tmp/test.db')
await db.open(':memory:')API reference
| Method | Returns | Description |
|---|---|---|
db.open(path) | Promise<number> | Open or create a database; returns handle |
db.close(handle?) | Promise<void> | Close and release connections |
db.setDefault(handle) | void | Set the handle used when none is passed |
db.query(sql, params?) | Promise<object[]> | SELECT — returns all rows |
db.run(sql, params?) | Promise<{rowsAffected, lastInsertId}> | INSERT / UPDATE / DELETE / DDL |
db.transaction(stmts) | Promise<void> | Atomic batch of {sql, params?} statements |
db.migrate(migrations) | Promise<number> | Apply pending {version, name, up} migrations |
db.seed(name?, fn) | Promise<void> | Run setup data (tracked or always) |
db.backup(destPath) | Promise<void> | Atomic online backup via VACUUM INTO |
db.config(opts) | void | Configure auto-backup schedule |
All methods accept an optional explicit handle as the first argument.
See the Database guide for full patterns and examples.