Query the experimental Turso engine
Start a self-hosted server and use its connection variables for these requests.
SQLite is the default SQL engine. The optional experimental-turso build feature adds an experimental HTTP API for named Turso databases on each disk. They use the Rust turso_core 0.8.3 engine and the disk’s existing storage. A Turso database and a SQLite database with the same name hold separate data. Existing SQLite endpoints and shell or JavaScript wabesql calls continue to use SQLite. Enabling the feature does not migrate or redirect SQLite databases.
Enable the routes
Section titled “Enable the routes”Build the embedded CLI with the feature before starting the server:
cargo build --locked --release -p wabe-disk-cli --features experimental-turso./target/release/wabe-disk serve --port 8090The default build returns 404 for the Turso routes. Authentication and server configuration apply to the enabled routes.
Endpoints
Section titled “Endpoints”POST /v1/disks/{disk}/turso/{database}/v3/pipelinePOST /v1/disks/{disk}/turso/{database}/v3/cursorSend Content-Type: application/json and the server Bearer key. The SDK base URL is /v1/disks/{disk}/turso/{database}/. The server opens or creates the named database on first access. Disk and database names contain 1–128 ASCII letters, digits, _ or -.
Parameters
Section titled “Parameters”The SQL JSON v3 request fields also apply here: pipeline operations, cursor batches, typed values, conditions, stored SQL IDs and continuation batons. Batch operations use explicit transaction statements or SDK transactions when atomicity is required. Each execute operation contains one statement.
Request
Section titled “Request”This request creates a table, inserts a row and reads it in the Turso database mydb:
curl --fail-with-body "$WABE_DISK_URL/v1/disks/demo/turso/mydb/v3/pipeline" \ -H "Authorization: Bearer $WABE_DISK_API_KEY" \ --json '{"requests":[{"type":"execute","stmt":{"sql":"CREATE TABLE IF NOT EXISTS notes(id INTEGER PRIMARY KEY, body TEXT)"}},{"type":"execute","stmt":{"sql":"INSERT INTO notes(body) VALUES(?)","args":[{"type":"text","value":"Hello from Turso"}]}},{"type":"execute","stmt":{"sql":"SELECT id, body FROM notes ORDER BY id"}},{"type":"close"}]}'The published @tursodatabase/serverless 1.4.1 driver accepts the same base URL. This JavaScript runs in the HTTP client:
import { connect } from "@tursodatabase/serverless";
const db = connect({ url: `${process.env.WABE_DISK_URL}/v1/disks/demo/turso/mydb/`, authToken: process.env.WABE_DISK_API_KEY,});db.defaultSafeIntegers(true);console.log(await db.all("SELECT id, body FROM notes ORDER BY id"));await db.close();Responses and persistence
Section titled “Responses and persistence”Pipeline returns JSON; cursor returns newline-delimited JSON. Response envelopes, typed values and baton rotation follow the SQL JSON v3 contract. Native SDK safe integers, named bindings, BLOB/NULL values, triggers, transactions, cursor iteration and Drizzle CRUD are covered by the experiment’s functional checks.
SQL COMMIT confirms buffered state. File sync does not force a separate object-store flush. The default background WAL interval is 10 seconds; explicit disk flush, eviction and graceful shutdown wait for persistence. An interrupted response does not establish whether a mutation committed.
Transactions retain their disk lease between requests. The transaction lifetime is 60 seconds; idle sessions outside transactions expire after 30 seconds. Closing a cursor early cancels its work and closes the session after cleanup.
Full-text search and change capture
Section titled “Full-text search and change capture”Turso FTS uses a custom index on the base table. It is enabled in this experimental build. Create the index with CREATE INDEX products_fts ON products USING fts(name) and search with fts_match(name, 'wireless'). The engine maintains the index on insert, update and delete. SQLite endpoints continue to use SQLite FTS5 and its SQL syntax.
CDC is off for a new connection. Enable it on the HTTP session that performs the writes with PRAGMA capture_data_changes_conn('full'). Continuations must use that session’s latest baton. A new session starts with CDC off even when the database already contains event rows. The allowed modes are off, id, before, after and full, using the default turso_cdc table. Custom CDC table names are outside this adapter’s policy.
This request creates a search index, enables CDC for the session, writes a product, searches it and reads its events:
curl --fail-with-body "$WABE_DISK_URL/v1/disks/demo/turso/mydb/v3/pipeline" \ -H "Authorization: Bearer $WABE_DISK_API_KEY" \ --json @- <<'JSON'{"requests":[ {"type":"execute","stmt":{"sql":"CREATE TABLE IF NOT EXISTS products(id INTEGER PRIMARY KEY,name TEXT)"}}, {"type":"execute","stmt":{"sql":"CREATE INDEX IF NOT EXISTS products_fts ON products USING fts(name)"}}, {"type":"execute","stmt":{"sql":"PRAGMA capture_data_changes_conn('full')"}}, {"type":"execute","stmt":{"sql":"INSERT INTO products VALUES(1,'wireless headphones') ON CONFLICT(id) DO UPDATE SET name=excluded.name"}}, {"type":"execute","stmt":{"sql":"SELECT id,name FROM products WHERE fts_match(name,'wireless') ORDER BY id"}}, {"type":"execute","stmt":{"sql":"SELECT change_id,change_type,id FROM turso_cdc WHERE table_name='products' ORDER BY change_id"}}, {"type":"close"}]}JSONCDC v2 records insert (1), update (0), delete (-1) and transaction completion (2). Event rows participate in the write transaction; rollback discards them. full captures binary before/after images and update details, while id captures identifiers without those images. Query pages with WHERE change_id > ? ORDER BY change_id LIMIT 100; change_txn_id groups transaction records. The binary records can be decoded with bin_record_json_object(table_columns_json_array('products'), after). Preserve the relevant schema when decoding events across schema changes.
These are SQL tables that the application polls and manages. The adapter does not automatically expire CDC rows. COMMIT and event rows use the same buffered durability policy described above; observing an event does not add a persistence barrier.
Errors and limits
Section titled “Errors and limits”The JSON v3 error shape and HTTP statuses follow SQL errors. Turso supplies primary SQLite-compatible result codes. Constraint errors use SQLITE_CONSTRAINT; this version does not supply the PRIMARYKEY or UNIQUE extended subcode. Protocol limits, policy failures and deadlines close the affected session.
The default Turso budget permits 16 active requests, with a 30-second SQL deadline. It is separate from the SQLite budget. Requests are limited to 4 MiB, statement text to 64 KiB and encoded results to 8 MiB. The same row, column, value and session budgets as the SQLite adapter apply. Turso execution allocations are not constrained by all of SQLite’s native engine limits; these request and result budgets do not form a query heap cap.
Attachments, extension loading, temporary-schema DDL and unsafe PRAGMAs are disabled. This experiment does not enable replication or multiprocess WAL. FTS uses Turso’s built-in fts index method; other custom index modules remain blocked. Turso’s native readonly classification is preserved; recursive CTEs can be classified as writing because they use temporary storage. The Rust query API can reject such a statement when readOnly is true.