Documentation Index
Fetch the complete documentation index at: https://docs.turso.tech/llms.txt
Use this file to discover all available pages before exploring further.
Turso offers two Rust crates:
| turso | libsql (legacy) |
|---|
| Use case | Local / embedded database, sync | Remote access, legacy embedded replicas |
| Engine | Turso Database (rewrite) | libSQL (SQLite fork) |
| Concurrent writes | Yes (MVCC) | Not supported |
| Sync | push/pull (true local-first) | Embedded replicas (writes go to remote) |
| C compiler | Not required | Required for core, replication, encryption features |
Starting a new project? Use turso for local/embedded use or sync. Use libsql with the remote feature for over-the-wire access (no C compiler needed).
turso
For local and embedded use. Built on the Turso Database engine with concurrent writes (MVCC) and async I/O.
Installing
cargo add turso tokio --features tokio/full
Connecting
use turso::Builder;
let db = Builder::new_local("app.db").build().await?;
let conn = db.connect()?;
In-memory databases are also supported:
let db = Builder::new_local(":memory:").build().await?;
Querying
conn.execute(
"CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL
)",
(),
).await?;
conn.execute("INSERT INTO users (name) VALUES (?)", ("Alice",)).await?;
let mut rows = conn.query("SELECT * FROM users", ()).await?;
while let Some(row) = rows.next().await? {
let id: i64 = row.get(0)?;
let name: String = row.get(1)?;
println!("User: {} {}", id, name);
}
Prepared Statements
let mut stmt = conn.prepare("SELECT * FROM users WHERE id = ?1").await?;
let mut rows = stmt.query([42]).await?;
Transactions
let tx = conn.transaction().await?;
tx.execute("INSERT INTO users (name) VALUES (?1)", ["Alice"]).await?;
tx.execute("INSERT INTO users (name) VALUES (?1)", ["Bob"]).await?;
tx.commit().await?;
Encryption
Encrypt local databases at rest:
use turso::{Builder, EncryptionOpts};
let db = Builder::new_local("encrypted.db")
.experimental_encryption(true)
.with_encryption(EncryptionOpts {
cipher: "aegis256".to_string(),
hexkey: "b1bbfda4f589dc9daaf004fe21111e00dc00c98237102f5c7002a5669fc76327".to_string(),
})
.build()
.await?;
Supported ciphers: aegis256, aegis256x2, aegis128l, aegis128x2, aegis128x4, aes256gcm, aes128gcm.
Encrypted databases cannot be read as standard SQLite databases — you must use the Turso Database engine to open them.
Turso Cloud databases can also be encrypted with bring-your-own-key — learn more.
Sync (Push and Pull)
For local database with cloud sync. All reads and writes happen locally; use push() to send changes to the cloud and pull() to fetch remote changes.
Enable the sync feature:
cargo add turso --features sync
use turso::sync::Builder;
let db = Builder::new_remote("app.db")
.with_remote_url(&std::env::var("TURSO_DATABASE_URL")?)
.with_auth_token(&std::env::var("TURSO_AUTH_TOKEN")?)
.bootstrap_if_empty(true) // Download schema on first sync (default)
.build()
.await?;
let conn = db.connect().await?;
On the first run, the local database is automatically bootstrapped from the remote. See Turso Sync for full details.
Push and Pull
// Push local writes to Turso Cloud
db.push().await?;
// Pull remote changes (returns true if changes were applied)
let changed = db.pull().await?;
Checkpoint
Compact the local WAL to bound disk usage while preserving sync state:
Stats
let stats = db.stats().await?;
println!("Network received: {} bytes", stats.network_received_bytes);
println!("Network sent: {} bytes", stats.network_sent_bytes);
println!("WAL size: {} bytes", stats.main_wal_size);
libsql (Remote)
Use the libsql crate with the remote feature for over-the-wire access to Turso Cloud. This uses pure Rust HTTP — no C compiler needed.
Installing
cargo add libsql --features remote
Connecting
use libsql::Builder;
let url = std::env::var("TURSO_DATABASE_URL")?;
let token = std::env::var("TURSO_AUTH_TOKEN")?;
let db = Builder::new_remote(url, token).build().await?;
let conn = db.connect()?;
Querying
let mut rows = conn.query("SELECT * FROM users WHERE id = ?1", [1]).await?;
libsql (Legacy)
The libsql crate is built on libSQL, our open-source fork of SQLite that predated the Turso Database engine. It is fully supported — if you run into something that doesn’t work yet with the turso crate, libsql is a reliable fallback.
With libsql embedded replicas, reads are local but writes go to the remote database. For true local-first writes with push/pull sync, use the turso crate with turso::sync — see the quickstart.
Embedded Replicas
Usage of embedded replicas is strongly discouraged. Use the turso crate with turso::sync instead — it is built on the Turso Database engine with better performance, true local-first writes, and concurrent write support.
You can work with embedded replicas that can sync from the remote database to a local SQLite file, and delegate writes to the remote primary database:
use libsql::Builder;
let url = std::env::var("TURSO_DATABASE_URL")?;
let token = std::env::var("TURSO_AUTH_TOKEN")?;
let db = Builder::new_remote_replica("local.db", url, token)
.build()
.await?;
let conn = db.connect()?;
Embedded Replicas only works where you have access to the file system.
Manual Sync
Sync Interval
use std::time::Duration;
let db = Builder::new_remote_replica("local.db", url, token)
.sync_interval(Duration::from_secs(300))
.build()
.await?;
Read Your Own Writes
By default, after a push(), the next pull() waits for the server to fully catch up with your changes before returning — guaranteeing you always read your own writes. This is safer but much slower, since the server must process all pending changes. If you can tolerate eventually-consistent reads, disable this for significantly faster pulls:
let db = Builder::new_remote_replica("local.db", url, token)
.read_your_writes(false)
.build()
.await?;
Encryption
For new projects, we recommend the turso crate for local encryption — it is built on the Turso Database engine with better performance and concurrent write support.
To enable encryption on a SQLite file, pass the encryption key value as an argument to the constructor:
use libsql::Builder;
use bytes::Bytes;
let url = env::var("LIBSQL_URL").expect("LIBSQL_URL must be set");
let token = env::var("LIBSQL_AUTH_TOKEN").unwrap_or_default();
let cipher = Cipher::YourChosenCipher;
let encryption_key_bytes = Bytes::from("your_secure_encryption_key_here");
let encryption_config = EncryptionConfig {
cipher,
encryption_key: encryption_key_bytes,
};
let mut db = Builder::new_remote_replica("local.db", &url, &token)
.encryption_config(encryption_config) // Apply encryption configuration
.build()
.await
.unwrap();
let conn = db.connect().unwrap();
Encrypted databases appear as raw data and cannot be read as standard SQLite databases. You must use the libSQL client for any operations — learn more.
Conditional compilation
The libsql crate supports conditionally compiling features:
| Feature | Description |
|---|
remote | HTTP-only client, pure Rust. No C compiler needed. |
core | Local database only. Requires a C compiler. |
replication | Combines core with embedded replica support. Requires a C compiler. |
encryption | Encryption at rest. Requires cmake. Not enabled by default. |
Simple query
conn.execute("SELECT * FROM users", ()).await?;
conn.execute("SELECT * FROM users WHERE id = ?1", [1]).await?;
Placeholders
conn.execute("SELECT * FROM users WHERE id = ?1", libsql::params![1]).await?;
Deserialization
use libsql::{de, Builder};
#[derive(Debug, serde::Deserialize)]
struct User {
name: String,
age: i64,
}
let mut stmt = conn.prepare("SELECT * FROM users WHERE id = ?1").await?;
let row = stmt.query([1]).await?.next().await?.unwrap();
let user = de::from_row::<User>(&row)?;
Batch Transactions
conn.execute_batch(r#"
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
INSERT INTO users (name) VALUES ('Alice');
INSERT INTO users (name) VALUES ('Bob');
"#).await?;
Interactive Transactions
let tx = conn.transaction().await?;
tx.execute("INSERT INTO users (name) VALUES (?1)", ["Alice"]).await?;
tx.execute("INSERT INTO users (name) VALUES (?1)", ["Bob"]).await?;
tx.commit().await?;