Elide ships three built-in database drivers, each exposed as a synthetic ESM
module under the elide: scheme:
Module
Import
Driver
Connection style
elide:sqlite
import { Database } from "elide:sqlite"
SQLite 3 (JDBC, embedded)
Synchronous; new Database(path?)
elide:postgres
import { Database } from "elide:postgres"
PostgreSQL (JDBC + HikariCP pool)
Asynchronous; await Database.connect(config)
elide:mysql
import { Database } from "elide:mysql"
MySQL (JDBC + HikariCP pool)
Asynchronous; await Database.connect(config)
There is no Elide.db global and no Elide.db.connect(); each driver is its
own module, imported by name. MongoDB and Redis are not supported, and there
is no ORM or query builder — these modules execute SQL directly.
Each module is registered lazily via ModuleRegistry.deferred(...) and resolves
only when imported; importing a module does not pollute the global scope.
elide:sqlite
SQLite is fully synchronous and embedded — there is no connection pool and no
network. You construct a Database directly.
The second argument is an options object (file-backed databases only):
Option
Type
Default
Effect
create
boolean
false
Create the file if it does not exist (parent directory must be writable).
readonly
boolean
false
Open the database read-only.
If a path is given that does not exist and create is not true, the
constructor throws FileNotFoundException.
Database methods
Method
Signature
Returns
query
query(sql, ...args)
Statement
prepare
prepare(sql, ...args)
Statement
exec
exec(sql, ...args)
undefined (runs without returning rows)
transaction
transaction(fn)
Transaction (a callable wrapper)
loadExtension
loadExtension(name)
loads a SQLite extension
serialize
serialize(schema?)
Uint8Array snapshot of the database
close
close(throwOnError?)
closes the database
query and prepare both return a Statement. The difference is intent:
query is for one-off execution, prepare for reuse. The statement is
prepared eagerly when arguments are passed.
Statement methods
Method
Signature
Returns
all
all(...args)
array of row objects
values
values(...args)
array of value arrays (one per row)
get
get(...args)
single row object, or undefined if none
run
run(...args)
executes the statement, returns nothing
Rows are returned as objects keyed by column name.
Example
javascript
import { Database } from"elide:sqlite";
const db = newDatabase(); // in-memory
db.exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)");
db.exec("INSERT INTO users (name) VALUES (?)", "Ada");
db.exec("INSERT INTO users (name) VALUES (?)", "Linus");
const all = db.query("SELECT * FROM users").all();
// → [ { id: 1, name: "Ada" }, { id: 2, name: "Linus" } ]const one = db.query("SELECT name FROM users WHERE id = ?", 1).get();
// → { name: "Ada" }
db.close();
Transactions wrap a function; the returned wrapper is invoked to run the
function inside BEGIN / COMMIT (rolling back on a thrown error):
javascript
const insertAll = db.transaction((names) => {
for (const name of names) db.exec("INSERT INTO users (name) VALUES (?)", name);
});
insertAll(["Grace", "Margaret"]);
elide:postgres and elide:mysql
PostgreSQL and MySQL share an identical JavaScript surface — only the import
name, default port, and SQL dialect differ. Both connect over a pooled JDBC
connection (HikariCP) and expose async methods that return Promises.
Database is a factory object with a single static method, connect, which
returns a Promise resolving to a database instance. It accepts either a
configuration object or a connection URL string.
PostgreSQL uses positional $1, $2, … placeholders (these must be sequential,
no gaps or repeats). MySQL uses ? placeholders. In both cases, bind values are
passed as trailing arguments to query / exec.
Example (PostgreSQL)
javascript
import { Database } from"elide:postgres";
const db = await Database.connect({
database: "myapp",
user: "postgres",
password: "secret",
});
// Readconst users = await db.query("SELECT * FROM users WHERE active = $1", true).all();
// Single rowconst user = await db.query("SELECT * FROM users WHERE id = $1", 42).get();
// Writeconst res = await db.exec("INSERT INTO users (name) VALUES ($1)", "Ada");
res.affectedRows; // → 1
res.insertId; // → generated key (or null)// Transactionawait db.transaction(async (tx) => {
await tx.exec("UPDATE accounts SET balance = balance - $1 WHERE id = $2", 100, 1);
await tx.exec("UPDATE accounts SET balance = balance + $1 WHERE id = $2", 100, 2);
});
await db.close();
Example (MySQL)
javascript
import { Database } from"elide:mysql";
const db = await Database.connect({
database: "myapp",
user: "root",
password: "secret",
});
const rows = await db.query("SELECT * FROM users WHERE name = ?", "Ada").all();
await db.exec("INSERT INTO users (name) VALUES (?)", "Linus");
await db.close();
A prepared Statement exposes query(...args), exec(...args),
executeBatch(argsList), and close().
Transactions
The callback passed to transaction receives a Transaction object. It is
committed when the callback resolves and rolled back if it throws (or its
returned Promise rejects). The transaction object exposes:
Member
Description
isolation
current isolation level (READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE)
active
whether the transaction is still open
query(sql, ...args) / exec(sql, ...args)
run statements in the transaction
commit() / rollback()
settle the transaction explicitly
savepoint(name) / rollbackTo(name)
nested savepoints
The default isolation level is READ_COMMITTED.
Notes
SQLite is synchronous; PostgreSQL and MySQL are asynchronous (Promise-based).
Connection pooling (PostgreSQL/MySQL) is provided by HikariCP and configured via the pool option.
Plain JavaScript objects cannot be bound as SQL parameters — use primitives, strings, byte arrays, or arrays.