Turso (SQLite на Edge) для веб-приложения
Turso — распределённая база данных на основе libSQL (форк SQLite), работающая на edge nodes по всему миру. Запросы выполняются на ближайшем узле к пользователю, latency < 10ms для read-heavy приложений. Embedded replicas позволяют запускать копию базы прямо в памяти приложения.
Архитектура
Turso использует модель primary + replicas:
- Primary — основная база, все записи идут сюда
- Replicas — read-only копии на edge (Frankfurt, Singapore, São Paulo и т.д.)
- Embedded Replica — локальная копия SQLite в памяти Cloudflare Worker/Node.js процесса
Для глобальных приложений с read-heavy нагрузкой (CMS, блоги, публичные API) embedded replica даёт ~0ms latency на чтение — запрос не выходит из runtime.
Настройка
# CLI
turso auth login
turso db create myapp --location ams # primary в Амстердаме
# Дополнительные реплики
turso db replicate myapp --location sin # Singapore
turso db replicate myapp --location gru # São Paulo
# Получить URL и токен
turso db show myapp --url
turso db tokens create myapp
// .env
TURSO_DATABASE_URL="libsql://myapp-org.turso.io"
TURSO_AUTH_TOKEN="eyJhbGciOiJFZERTQSJ9..."
libSQL клиент
import { createClient } from '@libsql/client';
const db = createClient({
url: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN!,
});
// Запросы — async/await
const result = await db.execute('SELECT * FROM articles WHERE published = 1 LIMIT 10');
console.log(result.rows);
// Параметризованные запросы
const post = await db.execute({
sql: 'SELECT * FROM articles WHERE slug = ?',
args: [slug],
});
// Транзакции
const tx = await db.transaction('write');
await tx.execute('INSERT INTO articles (title, body) VALUES (?, ?)', ['Hello', 'World']);
await tx.execute('INSERT INTO tags (article_id, name) VALUES (?, ?)', [1, 'news']);
await tx.commit();
Embedded Replica для zero-latency чтения
import { createClient } from '@libsql/client';
import { resolve } from 'path';
const db = createClient({
url: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN!,
syncUrl: process.env.TURSO_DATABASE_URL!, // синхронизироваться с primary
syncInterval: 60, // секунды — как часто подтягивать изменения
});
// Первый вызов — синхронизирует реплику
await db.sync();
// Все SELECT теперь из локального SQLite — 0ms
const posts = await db.execute('SELECT id, title FROM articles');
Embedded replica особенно эффективна для SSR на Cloudflare Workers: страница рендерится без сетевых запросов к базе.
Drizzle ORM + Turso
npm install drizzle-orm @libsql/client
npm install -D drizzle-kit
// db/schema.ts
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
export const articles = sqliteTable('articles', {
id: integer('id').primaryKey({ autoIncrement: true }),
title: text('title').notNull(),
slug: text('slug').notNull().unique(),
body: text('body'),
publishedAt: integer('published_at', { mode: 'timestamp' }),
});
// db/client.ts
import { drizzle } from 'drizzle-orm/libsql';
import { createClient } from '@libsql/client';
const client = createClient({
url: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN!,
});
export const db = drizzle(client);
// Запросы
const posts = await db.select().from(articles)
.where(isNotNull(articles.publishedAt))
.orderBy(desc(articles.publishedAt))
.limit(10);
Multi-tenancy с Turso
Turso поддерживает базы данных per tenant (Databases per Tenant model):
// Создать базу для каждого tenant через API
async function provisionTenantDB(tenantSlug: string) {
const response = await fetch(`https://api.turso.tech/v1/organizations/${ORG}/databases`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${TURSO_API_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: `tenant-${tenantSlug}`,
group: 'default',
}),
});
const { database } = await response.json();
// Создать токен доступа для tenant
const tokenRes = await fetch(
`https://api.turso.tech/v1/organizations/${ORG}/databases/${database.Name}/auth/tokens`,
{ method: 'POST', headers: { Authorization: `Bearer ${TURSO_API_TOKEN}` } }
);
const { jwt } = await tokenRes.json();
return { url: `libsql://${database.Name}-${ORG}.turso.io`, token: jwt };
}
До 10 000 баз на одном аккаунте — жизнеспособная модель для micro-SaaS.
Когда выбирать Turso
Подходит:
- Read-heavy глобальные приложения (< 5% writes)
- Cloudflare Workers / Deno Deploy (нет TCP, нужен HTTP)
- Micro-SaaS с database-per-tenant на SQLite
- Приложения с простой схемой без сложных JOIN
Не подходит:
- Write-heavy нагрузки (все записи идут в primary — нет шардирования)
- Сложные аналитические запросы (SQLite не ClickHouse)
- Требования к PostgreSQL-специфичным возможностям (RLS, advanced types, PostGIS)
- ACID транзакции на нескольких таблицах с высоким параллелизмом
Сроки
Настройка Turso, libSQL клиент + Drizzle ORM, embedded replica для Cloudflare Workers, CI миграции: 1–2 дня.







