Turso
Quickstart
Add durable, hosted database persistence to an existing Bapx project with the Turso blueprint. Run the following command in your terminal or coding agent of choice:
bapX add database turso
Overview
The Turso blueprint installs @bapX/libsql and @libsql/client, creates a source-root db.ts, and updates existing environment documentation when the project has it. It uses the libSQL adapter with Turso’s database URL and auth token:
import { libsql } from '@bapX/libsql';
import { createClient, type ResultSet } from '@libsql/client';
const client = createClient({
url: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN!,
});
const toRows = (rs: ResultSet) =>
rs.rows.map((row) => Object.fromEntries(rs.columns.map((column) => [column, row[column]])));
export default libsql({
query: async (text, params = []) => toRows(await client.execute({ sql: text, args: params })),
transaction: async (fn) => {
const tx = await client.transaction('write');
// ...
},
close: () => client.close(),
});
Bapx discovers the adapter at build time and wires it into the generated Node server. On startup, it creates or verifies the required bapX_* tables. Canonical agent conversations, immutable attachments, accepted submissions, and workflow history then survive process replacement in hosted Turso. Replicas may share durable state and workflow history, but each agent instance still requires one live Node owner. Application business data remains application-owned. The blueprint applies only to Node targets because Cloudflare deployments use Durable Object SQLite instead.
Configure
| Variable | Purpose |
|---|---|
TURSO_DATABASE_URL | Required — The database’s libsql:// URL. |
TURSO_AUTH_TOKEN | Required — Auth token for the database. |
createClient reads these at runtime — they are not baked into the build. For
local development, bapX dev --env <file> and bapX run --env <file> load any
.env-format file. In production, supply them from your platform’s secret store.
Turso is hosted, replicated libSQL. The blueprint installs @bapX/libsql and
the official @libsql/client, and writes a source-root db.ts that wraps the
client with a Turso configuration — it is the same adapter as
bapX add database libsql, pointed at a Turso
database. Bapx discovers db.ts at build time and wires it into the generated
Node server.
@bapX/libsql is a Node.js adapter. The Cloudflare target uses Durable
Object SQLite automatically and rejects a db.ts file at build time, so this
guide applies to Node deployments. See Database for the
full picture of how state is stored on each target.
Create a database
Create a database and an auth token with the Turso CLI:
turso db create bapX-agents
turso db show --url bapX-agents # → TURSO_DATABASE_URL (libsql://…)
turso db tokens create bapX-agents # → TURSO_AUTH_TOKEN
import { libsql } from '@bapX/libsql';
import { createClient, type ResultSet } from '@libsql/client';
const client = createClient({
url: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN!,
});
const toRows = (rs: ResultSet) =>
rs.rows.map((row) => Object.fromEntries(rs.columns.map((column) => [column, row[column]])));
export default libsql({
query: async (text, params = []) => toRows(await client.execute({ sql: text, args: params })),
transaction: async (fn) => {
const tx = await client.transaction('write');
try {
const result = await fn({
query: async (text, params = []) => toRows(await tx.execute({ sql: text, args: params })),
});
await tx.commit();
return result;
} catch (error) {
await tx.rollback();
throw error;
} finally {
tx.close();
}
},
close: () => client.close(),
});
Turso serializes writes server-side, so there is no embedded-file concurrency
concern. The runner shape (query, transaction, close) and the ResultSet
mapping are explained in the libSQL guide.
Embedded replicas
For lower read latency, Turso supports embedded replicas — a local SQLite
file kept in sync with the remote database, so reads hit local disk and writes
forward to Turso. Point url at a local file and add syncUrl:
const client = createClient({
url: 'file:bapX-replica.db',
syncUrl: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN!,
});
The rest of the db.ts is unchanged. Reach for this when read latency matters;
the plain remote client above is the default.
Migrations
The adapter’s migrate() hook runs automatically when the generated Node
server starts. It creates Bapx’s bapX_* tables idempotently and stamps a
schema version, so a fresh database is provisioned on first boot and an existing
one is reused on restart. There is no separate migration command to run, and a
database written by a newer Bapx refuses to start rather than corrupting state.
What gets stored
A Bapx database stores runtime state, not your whole application.
| Stored by Bapx | Not stored by Bapx |
|---|---|
| Canonical agent conversation streams and compaction records | Sandbox files and installed dependencies |
| Immutable attachment payloads | External API side effects |
Accepted direct prompts and dispatch(...) submissions | Application-owned business data unless your own tools store it |
| Workflow-run records and persisted events | Provider credentials or secrets |
Run indexing for /runs lookups and listRuns() |
See Durable Agents for how recovery uses submission state, and the Data Persistence API for the exact adapter contract.
When to choose Turso
Choose Turso when you want a managed, replicated SQLite without running a
server, and optionally embedded replicas for low-latency reads. For a local
file or a libSQL server you operate yourself, use the same adapter via the
libSQL guide. For external durable storage that supports process or host replacement, see @bapX/postgres. Node still requires one live owner per agent instance.