@bounda-dev/sqlite stores everything in SQLite through
libSQL. The same adapter opens a local
file, an in-memory database or a remote libSQL server such as Turso.
import { sqlite } from "@bounda-dev/sqlite";
sqlite({ path: "./data/app.db" });sqlite({ memory: true });sqlite({ url: "libsql://my-app-me.turso.io", authToken: process.env.TURSO_AUTH_TOKEN });Options
Section titled “Options”| Option | Default | Meaning |
|---|---|---|
path |
Path of the database file, created if missing | |
memory: true |
An in-memory database that lives as long as the app | |
url, authToken |
A libSQL URL (libsql://, https:// or file:) and its token |
|
tablePrefix |
bounda_ |
Put in front of every table Bounda creates |
How it stores things
Section titled “How it stores things”positionin the events table isINTEGER PRIMARY KEY AUTOINCREMENT. SQLite has a single writer, so the global order matches commit order without extra locking.- An append runs in
BEGIN IMMEDIATE … COMMIT: the stream version is checked and the events are written inside one transaction. A stale version rolls back and the command is retried with fresh state. - Claims for handlers and scheduled commands are single statements with
RETURNING, so two instances of your app on the same file never run the same handler for the same event at once. - Booleans are stored as
0/1, dates as ISO-8601 text, JSON fields as text. You never see that: rows come back typed from yourfields.
Within one process, on a file or in memory, the adapter runs its statements, and those a query
sends through client.raw, one after another around its write transactions, so they never wait
on a lock of their own. A file is opened in WAL mode, so other processes keep reading while one
writes, and a statement waits up to five seconds for another process’s write instead of failing
at once. A file not in WAL mode yet, such as one just restored from a backup, is switched by
whichever process opens it first; others starting at the same moment try again for those same
five seconds rather than fail. That is enough for a web process, a worker
and bounda rebuild on one file; for heavier contention, or for more than one machine, use
PostgreSQL.
The SQL itself is not specific to libSQL. The stores, the schema and the read models live in
@bounda-dev/core/adapter/sqlite, and createSqliteAdapter builds a complete adapter from any
SQLite connection a host brings: this package brings libSQL, and the same code runs inside a
Cloudflare Durable Object.
In tests
Section titled “In tests”createTestApp defaults to the in-memory adapter from @bounda-dev/core/memory, which runs no
SQL. Pass sqlite({ memory: true }) when a test needs the real thing, for example to exercise a
query written in SQL: see against a real database.