Skip to main content

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 three TypeScript packages:
@tursodatabase/database@tursodatabase/sync@tursodatabase/serverless@libsql/client (legacy)
Use caseLocal / embedded databaseLocal database + cloud syncRemote access (servers, containers, serverless, edge)Legacy — ORM support
EngineTurso Database (rewrite)Turso Database (rewrite)Turso DatabaselibSQL (SQLite fork)
DependenciesNative (Node.js, WASM)Native (Node.js)fetch only — zero native depsRequires Node.js or /web subpath
Concurrent writesYes (MVCC)Yes (MVCC)PlannedNot supported
Syncpush/pull (true local-first)Embedded replicas (writes go to remote)
ORM supportDrizzle (beta)Drizzle, Prisma, and others
Starting a new project? Use @tursodatabase/database for local/embedded use, @tursodatabase/sync for local + cloud sync, or @tursodatabase/serverless for any application that connects to a remote Turso Cloud database (Node.js servers, Docker containers, serverless functions, edge runtimes). Using an ORM? Use @libsql/client — it’s production-ready and supported by Drizzle, Prisma, and others. Drizzle has beta support for @tursodatabase/database for local/embedded use. The following runtime environments are known to be compatible:
  • Node.js version 12 or later
  • Deno
  • CloudFlare Workers
  • Netlify & Vercel Edge Functions

@tursodatabase/database

For local and embedded use. Built on the Turso Database engine with concurrent writes (MVCC) and async I/O.

Installing

npm install @tursodatabase/database

Initializing

import { connect } from "@tursodatabase/database";

const db = await connect("app.db");
In-memory databases are also supported:
const db = await connect(":memory:");

Querying

const stmt = db.prepare("SELECT * FROM users");
const users = await stmt.all();

const insert = db.prepare("INSERT INTO users (username) VALUES (?)");
await insert.run("alice");

Encryption

Encrypt local databases at rest using the encryption option:
import { connect } from "@tursodatabase/database";

const db = await connect("encrypted.db", {
  encryption: {
    cipher: "aegis256",
    hexkey: "b1bbfda4f589dc9daaf004fe21111e00dc00c98237102f5c7002a5669fc76327",
  },
});
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.

@tursodatabase/sync

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.

Installing

npm install @tursodatabase/sync

Initializing

import { connect } from "@tursodatabase/sync";

const db = await connect({
  path: "./app.db",
  url: process.env.TURSO_DATABASE_URL,
  authToken: process.env.TURSO_AUTH_TOKEN,
});
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
await db.push();

// Pull remote changes to local database
const changed = await db.pull();

Checkpoint

Compact the local WAL to bound disk usage while preserving sync state:
await db.checkpoint();

Stats

const s = await db.stats();
console.info({
  cdcOperations: s.cdcOperations,
  mainWalSize: s.mainWalSize,
  networkReceivedBytes: s.networkReceivedBytes,
  networkSentBytes: s.networkSentBytes,
  lastPullUnixTime: s.lastPullUnixTime,
  lastPushUnixTime: s.lastPushUnixTime,
  revision: s.revision,
});

@tursodatabase/serverless

The recommended package for any application that connects to a remote Turso Cloud database — Node.js servers, Docker containers, serverless functions (AWS Lambda, Vercel Functions), and edge runtimes (Cloudflare Workers, Deno Deploy). Uses only fetch — zero native dependencies, works everywhere fetch is available.

Installing

npm install @tursodatabase/serverless

Initializing

import { connect } from "@tursodatabase/serverless";

const conn = connect({
  url: process.env.TURSO_DATABASE_URL,
  authToken: process.env.TURSO_AUTH_TOKEN,
});
For compatibility with the @libsql/client API, use the compat module:
import { createClient } from "@tursodatabase/serverless/compat";

const client = createClient({
  url: process.env.TURSO_DATABASE_URL,
  authToken: process.env.TURSO_AUTH_TOKEN,
});

Querying

const stmt = await conn.prepare("SELECT * FROM users");
const rows = await stmt.all();

const stmt2 = await conn.prepare("SELECT * FROM users WHERE id = ?");
const row = await stmt2.get([1]);

@libsql/client (Legacy)

The @libsql/client package is built on libSQL, our open-source fork of SQLite that predated the Turso Database engine. It is fully supported and battle-tested — if you run into something that doesn’t work yet with the newer Turso packages, @libsql/client is a reliable fallback. It’s also the right choice if you need ORM integration beyond Drizzle (e.g., Prisma) or are working with an existing @libsql/client-based codebase.
With @libsql/client embedded replicas, reads are local but writes go to the remote database. For true local-first writes with push/pull sync, use @tursodatabase/sync with Turso Sync.

Installing

Begin by installing the @libsql/client dependency in your project:
npm install @libsql/client

Initializing

import { createClient } from "@libsql/client";

export const turso = createClient({
  url: process.env.TURSO_DATABASE_URL,
  authToken: process.env.TURSO_AUTH_TOKEN,
});

If you’re using libsql locally or an sqlite file, you can ignore passing authToken.

In-Memory Databases

import { createClient } from "@libsql/client";

const client = createClient({
  url: ":memory:",
});

Local Development

You can work locally using an SQLite file and passing the path to createClient:
import { createClient } from "@libsql/client";

const client = createClient({
  url: "file:path/to/db-file.db",
  authToken: "...",
});
The @libsql/client/web does not support local file URLs.

Embedded Replicas

For new projects, we recommend @tursodatabase/sync for sync use cases — 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 by passing your Turso Database URL to syncUrl:
import { createClient } from "@libsql/client";

const client = createClient({
  url: "file:path/to/db-file.db",
  syncUrl: "libsql://[databaseName]-[organizationSlug].turso.io",
  authToken: "...",
});
Embedded Replicas only works where you have access to the file system.

Manual Sync

await client.sync();

Periodic Sync

import { createClient } from "@libsql/client";

const client = createClient({
  url: "file:path/to/db-file.db",
  syncUrl: "libsql://[databaseName]-[organizationSlug].turso.io",
  syncInterval: 60,
  authToken: "...",
});

Encryption

For new projects, we recommend @tursodatabase/database for local encryption — it is built on the Turso Database engine with better performance and concurrent write support.
TypeScript
import { createClient } from "@libsql/client";

const db = createClient({
  url: "file:encrypted.db",
  encryptionKey: process.env.ENCRYPTION_KEY,
});
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.

Concurrency

By default, the client performs up to 20 concurrent requests:
import { createClient } from "@libsql/client";

const client = createClient({
  concurrency: 10,
});

Response

Each method listed below returns a Promise<ResultSet>:
PropertyTypeDescription
rowsArray<Row>An array of Row objects containing the row values, empty for write operations
columnsArray<string>An array of strings with the names of the columns in the order they appear in each Row, empty for write operations
rowsAffectednumberThe number of rows affected by a write statement, 0 otherwise
lastInsertRowidbigint | undefinedThe ID of a newly inserted row, or undefined if there is none for the statement

Simple query

You can pass a string or object to execute() to invoke a SQL statement:
const result = await client.execute("SELECT * FROM users");

Placeholders

libSQL supports the use of positional and named placeholders within SQL statements:
const result = await client.execute({
  sql: "SELECT * FROM users WHERE id = ?",
  args: [1],
});

const result = await client.batch(
  [
    {
      sql: "INSERT INTO users VALUES (?)",
      args: ["Iku"],
    },
  ],
  "write",
);

libSQL supports the same named placeholder characters as SQLite — :, @ and $.

Transaction Modes

ModeSQLite commandDescription
writeBEGIN IMMEDIATEThe transaction may execute statements that read and write data. Write transactions executed on a replica are forwarded to the primary instance, and can’t operate in parallel.
readBEGIN TRANSACTION READONLYThe transaction may only execute statements that read data (select). Read transactions can occur on replicas, and can operate in parallel with other read transactions.
deferredBEGIN DEFERREDThe transaction starts in read mode, then changes to write as soon as a write statement is executed. This mode change may fail if there is a write transaction currently executing on the primary.

Batch Transactions

A batch consists of multiple SQL statements executed sequentially within an implicit transaction. The backend handles the transaction: success commits all changes, while any failure results in a full rollback with no modifications.
const result = await client.batch(
  [
    {
      sql: "INSERT INTO users VALUES (?)",
      args: ["Iku"],
    },
    {
      sql: "INSERT INTO users VALUES (?)",
      args: ["Iku 2"],
    },
  ],
  "write",
);

Interactive Transactions

Interactive transactions in SQLite ensure the consistency of a series of read and write operations within a transaction’s scope. These transactions give you control over when to commit or roll back changes, isolating them from other client activity.
MethodDescription
execute()Similar to execute() except within the context of the transaction
commit()Commits all write statements in the transaction
rollback()Rolls back the entire transaction
close()Immediately stops the transaction
try {
  const userId = "user123";
  const withdrawalAmount = 500;

  const transaction = await client.transaction("write");

  const balanceResult = await transaction.execute({
    sql: "SELECT balance FROM accounts WHERE userId = ?",
    args: [userId],
  });

  const currentBalance = balanceResult.rows[0]["balance"] as number;

  if (currentBalance >= withdrawalAmount) {
    await transaction.execute({
      sql: "UPDATE accounts SET balance = balance - ? WHERE userId = ?",
      args: [withdrawalAmount, userId],
    });
  } else {
    console.log("Insufficient funds");
    await transaction.rollback();
    return;
  }

  await transaction.commit();
} catch (e) {
  console.error(e);
}

Interactive transactions in libSQL lock the database for writing until committed or rolled back, with a 5-second timeout. They can impact performance on high-latency or busy databases.

ATTACH

You can attach multiple databases to the current connection using the ATTACH attachment:
import { createClient } from "@libsql/client";

const client = createClient({
  url: process.env.TURSO_DATABASE_URL,
  authToken: process.env.TURSO_AUTH_TOKEN,
});

const txn = await db.transaction("read");

await txn.execute('ATTACH "<database-id>" AS attached');

const rs = await txn.execute("SELECT * FROM attached.users");
Make sure to allow ATTACH and create a token with the permission to attach a database — learn more