Turso/libSQL driver for ObjectStack — edge-first SQLite with embedded replicas, cloud-only remote mode, and database-per-tenant multi-tenancy.
TursoDriver implements a dual-transport architecture:
- Local/Replica modes: Extends
SqlDriverfrom@objectstack/driver-sql. All CRUD, schema, filtering, aggregation, window functions, introspection, and transactions are inherited via Knex + better-sqlite3. - Remote mode: Delegates all operations to
RemoteTransportwhich uses@libsql/clientSDK directly (HTTP/WebSocket). No local SQLite or Knex dependency needed.
TursoDriver extends SqlDriver (dual transport)
├── Transport: local/replica (via Knex + better-sqlite3)
│ ├── Inherited: find, findOne, create, update, delete, count, upsert
│ ├── Inherited: bulkCreate, bulkUpdate, bulkDelete, updateMany, deleteMany
│ ├── Inherited: syncSchema, dropTable, introspectSchema
│ ├── Inherited: aggregate, distinct, findWithWindowFunctions
│ ├── Inherited: beginTransaction, commit, rollback
│ └── Inherited: applyFilters (MongoDB-style + array-style)
├── Transport: remote (via @libsql/client)
│ ├── RemoteTransport: find, findOne, create, update, delete, count, upsert
│ ├── RemoteTransport: bulkCreate, bulkUpdate, bulkDelete, updateMany, deleteMany
│ ├── RemoteTransport: syncSchema, dropTable
│ ├── RemoteTransport: beginTransaction, commit, rollback
│ └── RemoteTransport: execute (raw SQL)
├── Override: name, version, supports (Turso-specific capabilities)
├── Override: connect / disconnect (transport-aware lifecycle)
├── Added: transportMode ('local' | 'replica' | 'remote')
├── Added: sync() — Embedded replica sync via @libsql/client
├── Added: Multi-tenant router with TTL cache
└── Added: TursoDriverConfig (url, authToken, syncUrl, mode, client)
pnpm add @objectstack/driver-tursoThe driver-turso package has different dependency requirements based on the connection mode:
| Mode | Required Dependencies | Notes |
|---|---|---|
| Remote | @libsql/client only |
✅ Vercel/Edge compatible — no native dependencies |
| Local | @libsql/client + better-sqlite3 |
Requires better-sqlite3 for local SQLite access |
| Replica | @libsql/client + better-sqlite3 |
Requires better-sqlite3 for local SQLite + sync |
For Vercel/Edge deployments (remote mode only):
pnpm add @objectstack/driver-turso
# better-sqlite3 is NOT requiredFor local/replica modes:
pnpm add @objectstack/driver-turso better-sqlite3The better-sqlite3 package is an optional peer dependency. If you're only using remote mode (e.g., on Vercel), you don't need to install it. npm/pnpm will show a warning that can be safely ignored.
import { TursoDriver } from '@objectstack/driver-turso';
const driver = new TursoDriver({
url: 'file:./data/app.db',
});
await driver.connect();const driver = new TursoDriver({
url: ':memory:',
});
await driver.connect();Local SQLite file + automatic sync from Turso cloud:
const driver = new TursoDriver({
url: 'file:./data/replica.db',
syncUrl: 'libsql://my-db-orgname.turso.io',
authToken: process.env.TURSO_AUTH_TOKEN,
sync: {
intervalSeconds: 60, // sync every 60 seconds
onConnect: true, // sync on initial connect
},
});
await driver.connect();
// Manual sync
await driver.sync();Pure remote queries via @libsql/client — no local SQLite needed.
Ideal for Vercel, Cloudflare Workers, and other serverless/edge runtimes:
const driver = new TursoDriver({
url: 'libsql://my-db-orgname.turso.io',
authToken: process.env.TURSO_AUTH_TOKEN,
});
await driver.connect();
// All CRUD operations work the same as local mode
const users = await driver.find('users', { where: { active: true } });Transport mode is automatically detected from the URL:
| URL Pattern | Mode | Engine |
|---|---|---|
file:./data/app.db |
local |
Knex + better-sqlite3 |
:memory: |
local |
Knex + better-sqlite3 |
file:... + syncUrl |
replica |
Knex + @libsql/client sync |
libsql://... |
remote |
@libsql/client only |
https://... |
remote |
@libsql/client only |
You can also force a specific mode:
const driver = new TursoDriver({
url: 'libsql://my-db.turso.io',
authToken: process.env.TURSO_AUTH_TOKEN,
mode: 'remote', // Force remote mode
});Pass a pre-configured @libsql/client instance for advanced use cases
(custom caching, connection pooling, testing):
import { createClient } from '@libsql/client';
const client = createClient({
url: 'libsql://my-db.turso.io',
authToken: process.env.TURSO_AUTH_TOKEN,
});
const driver = new TursoDriver({
url: 'libsql://my-db.turso.io',
client, // Inject pre-configured client
});
await driver.connect();Database-per-tenant architecture with automatic driver caching:
import { createMultiTenantRouter } from '@objectstack/driver-turso';
const router = createMultiTenantRouter({
urlTemplate: 'file:./data/{tenant}.db',
clientCacheTTL: 300_000, // 5 minutes
onTenantCreate: async (tenantId) => {
console.log(`Provisioned database for tenant: ${tenantId}`);
},
});
// In a request handler:
const driver = await router.getDriverForTenant('acme');
const users = await driver.find('users', { where: { active: true } });
// Cleanup on shutdown
await router.destroyAll();Both urlTemplate and driverConfigOverrides.syncUrl support {tenant} placeholder interpolation:
const router = createMultiTenantRouter({
urlTemplate: 'file:./data/{tenant}-replica.db',
groupAuthToken: process.env.TURSO_GROUP_TOKEN,
driverConfigOverrides: {
syncUrl: 'libsql://{tenant}-myorg.turso.io',
sync: { intervalSeconds: 30 },
},
});Concurrent getDriverForTenant() calls for the same tenant are deduplicated — only one driver is created, and all callers share the same instance.
interface TursoDriverConfig {
/**
* Database URL.
* - file:./data/local.db → local mode
* - :memory: → local mode (ephemeral)
* - libsql://my-db.turso.io → remote mode
* - https://my-db.turso.io → remote mode
*/
url: string;
/** JWT auth token for the remote Turso database */
authToken?: string;
/**
* AES-256 encryption key for local database file.
* Only effective in local/replica modes.
*/
encryptionKey?: string;
/**
* Maximum concurrent requests to the remote database.
* Effective in replica and remote modes.
* Default: 20
*/
concurrency?: number;
/** Remote sync URL for embedded replica mode (libsql:// or https://) */
syncUrl?: string;
/** Sync configuration (requires syncUrl) */
sync?: {
intervalSeconds?: number; // Default: 60
onConnect?: boolean; // Default: true
};
/**
* Operation timeout in milliseconds for remote operations.
* Effective in replica and remote modes.
*/
timeout?: number;
/**
* Force a specific transport mode.
* If not set, mode is auto-detected from the URL.
*/
mode?: 'local' | 'replica' | 'remote';
/**
* Pre-configured @libsql/client instance.
* Useful for custom caching, connection pooling, or testing.
*/
client?: Client;
}TursoDriver declares enhanced capabilities beyond the base SqlDriver:
| Capability | SqlDriver | TursoDriver (local) | TursoDriver (remote) |
|---|---|---|---|
| FTS5 Full-Text Search | ❌ | ✅ | ✅ |
| JSON1 Query | ❌ | ✅ | ✅ |
| Common Table Expressions | ❌ | ✅ | ✅ |
| Savepoints | ❌ | ✅ | ✅ |
| Indexes | ❌ | ✅ | ✅ |
| Connection Pooling | ✅ | ❌ (concurrency limits) | ❌ |
| Embedded Replica Sync | — | ✅ | — |
| Multi-Tenant Routing | — | ✅ | ✅ |
| Serverless/Edge | — | — | ✅ |
import tursoPlugin from '@objectstack/driver-turso';
// Via plugin system
await kernel.enablePlugin(tursoPlugin, {
url: 'file:./data/app.db',
});pnpm test # Run all testsTests run against in-memory SQLite (:memory:) — no external services required.
Apache-2.0 — Copyright (c) 2025 ObjectStack