← All articles
DATABASES Turso and libSQL: SQLite at the Edge 2026-02-14 · 6 min read · turso · libsql · sqlite

Turso and libSQL: SQLite at the Edge

Databases 2026-02-14 · 6 min read turso libsql sqlite database edge typescript serverless replication

Turso and libSQL: SQLite at the Edge

Turso libSQL edge database logo

SQLite is the most deployed database in the world. It is embedded in every smartphone, every browser, and countless server applications. But it has one fundamental limitation for web applications: it runs on a single machine. If your application runs across multiple servers or edge locations, you cannot share a SQLite file between them.

Turso solves this by building on libSQL, an open-source fork of SQLite that adds network access, replication, and multi-tenancy. The result is a database that gives you SQLite's simplicity and performance with the distribution capabilities of a client-server database. Your data lives close to your users, reads are local, and writes propagate through a replication protocol.

What Is libSQL?

libSQL is a fork of SQLite maintained by the Turso team. It is fully compatible with SQLite -- any SQLite database file works with libSQL, and any SQLite client library can connect to it. But libSQL adds capabilities that SQLite's project governance does not allow upstream:

Turso: The Managed Platform

Turso is the managed platform for libSQL. It handles provisioning, replication, backups, and access control. You interact with it through the CLI or dashboard and connect from your application via an HTTP-based protocol.

Getting Started

# Install the Turso CLI
curl -sSfL https://get.tur.so/install.sh | bash

# Authenticate
turso auth login

# Create a database
turso db create my-app-db

# Specify a primary location (closest to your write workload)
turso db create my-app-db --location sea  # Seattle

Working with the CLI

# List databases
turso db list

# Open an interactive shell
turso db shell my-app-db

# Run a query directly
turso db shell my-app-db "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)"
turso db shell my-app-db "INSERT INTO users (name, email) VALUES ('Alice', '[email protected]')"
turso db shell my-app-db "SELECT * FROM users"

# Get the connection URL and auth token
turso db show my-app-db --url
turso db tokens create my-app-db

Adding Edge Replicas

Replicas bring read copies of your database to other regions:

# Add replicas in other locations
turso db replicate my-app-db lhr  # London
turso db replicate my-app-db nrt  # Tokyo
turso db replicate my-app-db gru  # Sao Paulo

# List locations
turso db show my-app-db --locations

Reads are served from the nearest replica. Writes are forwarded to the primary and then replicated outward. This gives you single-digit millisecond read latency globally while maintaining consistency.

TypeScript SDK

The @libsql/client package is the official TypeScript/JavaScript SDK. It works in Node.js, Bun, Deno, and edge runtimes like Cloudflare Workers.

Installation

# npm
npm install @libsql/client

# Bun
bun add @libsql/client

Connecting

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

const db = createClient({
  url: "libsql://my-app-db-username.turso.io",
  authToken: process.env.TURSO_AUTH_TOKEN,
});

Queries

// Simple query
const result = await db.execute("SELECT * FROM users WHERE active = 1");
console.log(result.rows);
// [{ id: 1, name: "Alice", email: "[email protected]" }, ...]

// Parameterized query (prevents SQL injection)
const user = await db.execute({
  sql: "SELECT * FROM users WHERE id = ?",
  args: [userId],
});

// Named parameters
const users = await db.execute({
  sql: "SELECT * FROM users WHERE name = :name AND role = :role",
  args: { name: "Alice", role: "admin" },
});

// Insert and get the last inserted ID
const insert = await db.execute({
  sql: "INSERT INTO users (name, email) VALUES (?, ?)",
  args: ["Bob", "[email protected]"],
});
console.log(insert.lastInsertRowid); // 2n (BigInt)

Transactions

const tx = await db.transaction("write");

try {
  await tx.execute({
    sql: "INSERT INTO orders (user_id, total) VALUES (?, ?)",
    args: [userId, 99.99],
  });
  await tx.execute({
    sql: "UPDATE users SET order_count = order_count + 1 WHERE id = ?",
    args: [userId],
  });
  await tx.commit();
} catch (e) {
  await tx.rollback();
  throw e;
}

Batch Queries

Send multiple statements in a single HTTP round-trip:

const results = await db.batch([
  { sql: "INSERT INTO logs (message) VALUES (?)", args: ["event_a"] },
  { sql: "INSERT INTO logs (message) VALUES (?)", args: ["event_b"] },
  { sql: "SELECT count(*) as total FROM logs", args: [] },
]);

console.log(results[2].rows[0].total); // Total log count

Embedded Replicas

The most powerful feature of Turso is embedded replicas. Instead of connecting to a remote database over the network, you can embed a local SQLite replica directly in your application. Reads hit the local file. Writes are forwarded to the primary and then synced back.

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

const db = createClient({
  url: "libsql://my-app-db-username.turso.io",
  authToken: process.env.TURSO_AUTH_TOKEN,
  syncUrl: "libsql://my-app-db-username.turso.io",
  syncInterval: 60, // Sync every 60 seconds
});

// Force a sync
await db.sync();

// Reads are now local (microseconds, not milliseconds)
const result = await db.execute("SELECT * FROM products WHERE category = 'tools'");

Embedded replicas give you:

This pattern is ideal for read-heavy applications, edge deployments, and mobile backends.

Multi-Tenant Patterns

Turso supports a database-per-tenant architecture where each customer gets their own isolated database. This is SQLite's natural strength -- each database is a separate file with zero overhead when idle.

# Create a database per tenant
turso db create tenant-acme
turso db create tenant-globex
turso db create tenant-initech

In your application, route requests to the appropriate database:

function getDbForTenant(tenantId: string) {
  return createClient({
    url: `libsql://tenant-${tenantId}-username.turso.io`,
    authToken: process.env.TURSO_AUTH_TOKEN,
  });
}

// Each tenant has completely isolated data
const acmeDb = getDbForTenant("acme");
const globexDb = getDbForTenant("globex");

Turso supports up to 10,000 databases on the free tier and millions on paid plans. Each idle database consumes zero compute resources.

Turso vs. PlanetScale vs. Neon

Feature Turso (libSQL) PlanetScale (MySQL) Neon (PostgreSQL)
Base engine SQLite MySQL PostgreSQL
Edge replicas Built-in Limited Read replicas
Embedded replicas Yes No No
Multi-tenant DB per tenant Schema per tenant DB per tenant
Cold start None (SQLite) Seconds Seconds
Branching Yes Yes Yes
Free tier 9 GB, 500 DBs Deprecated 0.5 GB
Best for Edge apps, multi-tenant MySQL-native apps Postgres-native apps

Choose Turso when: You want SQLite simplicity, need edge distribution, or are building a multi-tenant SaaS where database-per-tenant isolation makes sense.

Choose Neon when: Your application is built on PostgreSQL and you need Postgres-specific features (advanced indexing, extensions, JSON operators, full-text search).

Choose PlanetScale when: Your application is MySQL-native and you need MySQL compatibility.

Practical Tips

Turso and libSQL make a compelling case for SQLite as a distributed database. The combination of SQLite's simplicity, edge replication, and embedded replicas creates an architecture that is both simpler and faster than traditional client-server databases for many workloads. If your application can model its data in SQLite, Turso removes the scaling barriers that previously made SQLite impractical for production web applications.