Skip to main content
Turso Tutorial: Getting Started with Edge SQLite (libSQL)

Turso Tutorial: Getting Started with Edge SQLite (libSQL)

KU
• 7 min read•Updated •59 views

A hands-on walkthrough of Turso and libSQL, from CLI setup to embedded replicas that put SQLite reads next to your users.

Key takeaways

  • Install the Turso CLI, run turso db create, then copy the libsql:// URL and an auth token into @libsql/client.
  • For fast reads, point the client at a local file with syncUrl set to the remote database, which makes it an embedded replica.
  • Writes still go to one remote primary, so plan around that.

To start with Turso, install the CLI, run turso db create, grab the libsql:// URL and a token, and query it from @libsql/client. For low-latency reads, give the client a local file: URL plus a syncUrl: that turns it into an embedded replica, a real SQLite file that syncs from the remote primary.

Verified against

Turso CLI v1.0.34, @libsql/client 0.18.0, against the docs.turso.tech CLI, embedded-replica and local-development docs and the turso.tech pricing page. Checked October 2026.

Turso is a managed service built on libSQL, an open-source fork of SQLite that adds a network protocol (sqld) and a sync engine. The mental model that matters: one remote primary takes writes, and local or embedded copies serve reads. Everything below follows from that. For where it sits among the alternatives, see our serverless and edge databases guide.

Install the CLI and get a database

The install script is the documented path on macOS and Linux; Homebrew works too (brew install tursodatabase/tap/turso). Windows needs WSL.

code
$ curl -sSfL https://get.tur.so/install.sh | bash
$ turso auth signup          # or: turso auth login
$ turso auth whoami
$ turso db create edge-demo
$ turso db show edge-demo --url
libsql://edge-demo-<org>.turso.io
$ turso db tokens create edge-demo
eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...
$ turso db shell edge-demo

Open a new shell after installing so turso is on your PATH. Store the URL and token as TURSO_DATABASE_URL and TURSO_AUTH_TOKEN. The token is a bearer credential; never commit it, and create one per app so you can revoke them independently.

Connect from Node and TypeScript

code
$ npm install @libsql/client
$ npm install -D tsx
TypeScript
// db.ts
import { createClient } from "@libsql/client";

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

await db.execute(`
  CREATE TABLE IF NOT EXISTS notes (
    id INTEGER PRIMARY KEY,
    body TEXT NOT NULL,
    created_at TEXT DEFAULT (datetime('now'))
  )`);

// Bound parameters, never string interpolation
await db.execute({
  sql: "INSERT INTO notes (body) VALUES (?)",
  args: ["First note from the edge"],
});

const result = await db.execute("SELECT id, body, created_at FROM notes");
console.table(result.rows);
code
$ TURSO_DATABASE_URL=libsql://edge-demo-<org>.turso.io TURSO_AUTH_TOKEN=... npx tsx db.ts

Every execute here is a network round trip to the primary. Fine for a script; for a read-heavy app you want the data on local disk. As of October 2026, Turso's TypeScript quickstart also points new projects at @tursodatabase/serverless for fetch-only runtimes. @libsql/client remains the client that supports embedded replicas, which is why we use it here.

Embedded replicas: the headline feature

An embedded replica is a local SQLite file that libSQL keeps in sync with the remote database. Reads hit the file at in-process speed. Writes go to the remote primary first, and on success the local file is updated.

TypeScript
// replica.ts
import { createClient } from "@libsql/client";

const db = createClient({
  url: "file:local.db",                      // reads come from here
  syncUrl: process.env.TURSO_DATABASE_URL!,  // remote primary
  authToken: process.env.TURSO_AUTH_TOKEN!,
  syncInterval: 60,                          // background pull, seconds
});

await db.sync(); // pull before the first read-critical path

const rows = await db.execute("SELECT count(*) AS n FROM notes");
console.log(rows.rows[0].n);

Turso's docs make one point many tutorials get wrong: the replica that performed a write sees it immediately, without calling sync(). Other replicas (another server, another process with its own file) only see it after their next sync() or interval tick. So read-your-writes is safe within one instance and eventual across instances.

As of October 2026 the docs also recommend Turso Sync for new projects that need local-first writes with explicit push() and pull(), while describing embedded replicas as fully supported in production. If your app only needs fast reads, embedded replicas are the simpler choice.

Local development without the network

Two options. Point createClient at a plain file:dev.db and skip the network entirely, or run a local libSQL server that speaks the same protocol as production:

code
$ turso dev --db-file dev.db
TypeScript
const db = createClient({ url: "http://127.0.0.1:8080" });

Without --db-file, data is lost when the server stops. Turso does not ship a migration framework; because the engine is SQLite, Drizzle Kit, Prisma and Atlas work through the libSQL driver. Keep migrations as ordered SQL files and apply them to the primary. If you are weighing plain SQLite on a VM instead, our notes on running SQLite in production cover what you give up.

Pricing, as of October 2026

The turso.tech pricing page lists a Free plan with 100 databases, 5 GB of storage, 500 million rows read and 10 million rows written per month, and 3 GB of syncs. Developer is $4.99/month with unlimited databases and 9 GB of storage; Scaler is $24.92/month and Pro $416.58/month, both with overage pricing per GB and per row. The page marks these as discounted rates, which points to yearly billing. Unlimited databases on paid plans is what makes the database-per-tenant pattern practical.

Failure modes and the exact errors

  • SERVER_ERROR: Server returned HTTP status 401: the token is missing, expired, or for another database. Regenerate with turso db tokens create <db> and check that the env var is actually set in the process.
  • URL_INVALID or connection refused on libsql://: you passed the HTTP URL from a dashboard copy or a typo. Use exactly what turso db show <db> --url prints.
  • turso: command not found after install: the installer edited your shell profile; open a new shell.
  • Corrupted or locked local.db: the docs warn not to open the replica file with another process while it is syncing. Do not point sqlite3 or a second client at the same file during sync.
  • Sync traffic bigger than expected: libSQL syncs 4 kB frames, so a one-byte row change still moves 4 kB, and B-tree splits or a deleted local file can trigger far larger re-syncs. Watch the syncs quota on the Free plan.
  • Embedded replica fails on serverless: platforms without a writable filesystem cannot host the local file. Use the remote URL there.

Where this breaks

All writes go to one primary. If your writers are spread across continents, every far-away write pays the round trip, and the edge story applies only to reads. Heavy write workloads, multi-statement transactions spanning regions, and features SQLite lacks (row-level security, rich types, extensions you rely on in Postgres) all argue for a Postgres-class system instead. For the head-to-head with Cloudflare's take on the same idea, read Turso vs Cloudflare D1.

Questions people ask

Is Turso free to use?

Yes, within limits. As of October 2026 the Free plan includes 100 databases, 5 GB of storage, 500 million rows read and 10 million rows written per month, plus 3 GB of sync traffic, with no card required. Paid plans start at $4.99/month (yearly billing) for unlimited databases and higher quotas. Check the pricing page before designing around a limit, because quotas have changed over time.

What is the difference between Turso and libSQL?

libSQL is the open-source fork of SQLite that adds a server (sqld), a remote protocol and replication. Turso is the company and managed cloud that runs libSQL databases for you, with the CLI, auth tokens and billing. You can self-host libSQL, but then the hosting, backups and token issuance are your problem rather than Turso's.

Do embedded replicas give read-your-writes consistency?

Within one replica, yes. Turso's docs state that after a successful write, the replica that made the write sees the new data immediately, even without calling sync(). Other replicas only see it after their own sync() or the next syncInterval tick. If a user can hit a different instance on the next request, read from the primary or call sync() first.

The call we'd make

For read-heavy apps that want SQLite simplicity, Turso is a strong default, and embedded replicas are the reason. Put the primary near your writers, use embedded replicas where reads cluster, and treat cross-instance staleness as a design constraint from day one. If your app is write-heavy or needs cross-region transactional guarantees, pick Postgres and stop fighting the model.

Sources

Explore topics:Cloud
React
Share this post
Cite this article
Plain text

Kiril Urbonas (2026). Turso Tutorial: Getting Started with Edge SQLite (libSQL). DevOpsNess. Updated 2026-10-31. https://www.devopsness.com/blog/turso-tutorial

Markdown

[Turso Tutorial: Getting Started with Edge SQLite (libSQL)](https://www.devopsness.com/blog/turso-tutorial) (Kiril Urbonas, DevOpsNess, updated 2026-10-31)

KU

Kiril Urbonas

AI Engineer

585 articles
View all articles by Kiril Urbonas

You might have missed

Evergreen posts worth revisiting.