From futuregerald-claude-plugin
Turso and libSQL best practices for SQLite-compatible cloud database development with edge distribution, embedded replicas, and vector search.
npx claudepluginhub futuregerald/futuregerald-claude-pluginThis skill uses the workspace's default tool permissions.
Turso is a fully managed SQLite-compatible database platform built on libSQL, a fork of SQLite. It provides edge distribution, embedded replicas, native vector search, branching, and point-in-time recovery. Core principle: **SQLite simplicity with cloud-scale distribution**.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Generates original PNG/PDF visual art via design philosophy manifestos for posters, graphics, and static designs on user request.
Turso is a fully managed SQLite-compatible database platform built on libSQL, a fork of SQLite. It provides edge distribution, embedded replicas, native vector search, branching, and point-in-time recovery. Core principle: SQLite simplicity with cloud-scale distribution.
| Task | Command/Pattern |
|---|---|
| Install CLI (macOS) | brew install tursodatabase/tap/turso |
| Install CLI (Linux) | curl -sSfL https://get.tur.so/install.sh | bash |
| Login | turso auth login |
| Create database | turso db create my-db |
| Connect to shell | turso db shell my-db |
| Get credentials | turso db show my-db --url and turso db tokens create my-db |
| Local dev server | turso dev |
| Local with file | turso dev --db-file local.db |
| Create branch | turso db create branch-db --from-db my-db |
| Point-in-time restore | turso db create restored --from-db my-db --timestamp 2024-01-01T00:00:00Z |
| Database dump | turso db shell my-db .dump > dump.sql |
# macOS
brew install tursodatabase/tap/turso
# Linux / Windows (WSL)
curl -sSfL https://get.tur.so/install.sh | bash
# Sign up (opens browser)
turso auth signup
# Login (opens browser)
turso auth login
# Headless mode (WSL/CI)
turso auth login --headless
# Create database (auto-detects closest region)
turso db create my-db
# Show database info
turso db show my-db
# Get connection URL
turso db show my-db --url
# Create auth token
turso db tokens create my-db
# Connect to shell
turso db shell my-db
npm install @libsql/client
# or
pnpm add @libsql/client
import { createClient } from '@libsql/client'
const client = createClient({
url: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN,
})
// Simple query
const result = await client.execute('SELECT * FROM users')
// Positional placeholders
const result = await client.execute({
sql: 'SELECT * FROM users WHERE id = ?',
args: [1],
})
// Named placeholders (:, @, or $)
const result = await client.execute({
sql: 'INSERT INTO users (name, email) VALUES (:name, :email)',
args: { name: 'Alice', email: 'alice@example.com' },
})
interface ResultSet {
rows: Array<Row> // Row data (empty for writes)
columns: Array<string> // Column names
rowsAffected: number // Affected rows (writes)
lastInsertRowid?: bigint // Last inserted row ID
}
Batch executes multiple statements in an implicit transaction:
const results = await client.batch(
[
{ sql: 'INSERT INTO users (name) VALUES (?)', args: ['Alice'] },
{ sql: 'INSERT INTO users (name) VALUES (?)', args: ['Bob'] },
],
'write' // Transaction mode: "write" | "read" | "deferred"
)
For complex logic with conditional commits/rollbacks:
const transaction = await client.transaction('write')
try {
const balance = await transaction.execute({
sql: 'SELECT balance FROM accounts WHERE id = ?',
args: [userId],
})
if (balance.rows[0].balance >= amount) {
await transaction.execute({
sql: 'UPDATE accounts SET balance = balance - ? WHERE id = ?',
args: [amount, userId],
})
await transaction.commit()
} else {
await transaction.rollback()
}
} catch (e) {
await transaction.rollback()
throw e
}
| Mode | SQLite Command | Description |
|---|---|---|
write | BEGIN IMMEDIATE | Read/write, serialized on primary |
read | BEGIN TRANSACTION READONLY | Read-only, can run on replicas in parallel |
deferred | BEGIN DEFERRED | Starts as read, upgrades to write on first write |
const client = createClient({
url: 'file:local.db',
})
No auth token needed. Works with standard SQLite features.
# Start local libSQL server
turso dev
# With persistent file
turso dev --db-file local.db
const client = createClient({
url: 'http://127.0.0.1:8080',
})
Supports all libSQL features including extensions.
# Export production data
turso db shell prod-db .dump > dump.sql
# Create local file from dump
cat dump.sql | sqlite3 local.db
const client = createClient({
url: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN, // undefined locally
})
# Production
TURSO_DATABASE_URL=libsql://my-db-org.turso.io
TURSO_AUTH_TOKEN=eyJ...
# Development
TURSO_DATABASE_URL=file:local.db
# No auth token needed
Local database that syncs with remote Turso database. Reads are instant (local), writes go to remote.
const client = createClient({
url: 'file:replica.db', // Local file
syncUrl: 'libsql://my-db.turso.io', // Remote primary
authToken: '...',
syncInterval: 60, // Auto-sync every 60 seconds
})
await client.sync()
const client = createClient({
url: 'file:replica.db',
syncUrl: 'libsql://my-db.turso.io',
authToken: '...',
offline: true, // Writes go to local, sync later
})
Native vector search without extensions.
CREATE TABLE movies (
id INTEGER PRIMARY KEY,
title TEXT,
embedding F32_BLOB(384) -- 384-dimensional float32 vector
);
| Type | Storage | Description |
|---|---|---|
FLOAT64 / F64_BLOB | 8D + 1 bytes | 64-bit double precision |
FLOAT32 / F32_BLOB | 4D bytes | 32-bit single precision (recommended) |
FLOAT16 / F16_BLOB | 2D + 1 bytes | 16-bit half precision |
FLOAT8 / F8_BLOB | D + 14 bytes | 8-bit compressed |
FLOAT1BIT / F1BIT_BLOB | D/8 + 3 bytes | 1-bit binary |
INSERT INTO movies (title, embedding)
VALUES ('Inception', vector32('[0.1, 0.2, 0.3, ...]'));
SELECT title,
vector_distance_cos(embedding, vector32('[0.1, 0.2, ...]')) AS distance
FROM movies
ORDER BY distance ASC
LIMIT 10;
-- Create index
CREATE INDEX movies_idx ON movies(libsql_vector_idx(embedding));
-- Query with index (much faster for large tables)
SELECT title
FROM vector_top_k('movies_idx', vector32('[0.1, 0.2, ...]'), 10)
JOIN movies ON movies.rowid = id;
CREATE INDEX movies_idx ON movies(
libsql_vector_idx(embedding, 'metric=cosine', 'compress_neighbors=float8')
);
| Setting | Values | Description |
|---|---|---|
metric | cosine, l2 | Distance function |
max_neighbors | integer | Graph connectivity |
compress_neighbors | vector type | Compression for storage |
search_l | integer | Search precision vs speed |
npm install drizzle-orm @libsql/client
npm install -D drizzle-kit
// drizzle.config.ts
import type { Config } from 'drizzle-kit'
export default {
schema: './db/schema.ts',
out: './migrations',
dialect: 'turso',
dbCredentials: {
url: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN,
},
} satisfies Config
// db/schema.ts
import { text, integer, sqliteTable } from 'drizzle-orm/sqlite-core'
export const users = sqliteTable('users', {
id: integer('id').primaryKey({ autoIncrement: true }),
name: text('name').notNull(),
email: text('email').notNull().unique(),
})
import { drizzle } from 'drizzle-orm/libsql'
import { createClient } from '@libsql/client'
const turso = createClient({
url: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN,
})
export const db = drizzle(turso)
# Generate migrations
npm run drizzle-kit generate
# Apply migrations
npm run drizzle-kit migrate
turso db create feature-branch --from-db production-db
turso db create restored-db --from-db production-db --timestamp 2024-01-15T10:00:00Z
name: Create Database Branch
on: create
jobs:
create-branch:
runs-on: ubuntu-latest
steps:
- name: Create Database
run: |
curl -X POST \
-H "Authorization: Bearer ${{ secrets.TURSO_API_TOKEN }}" \
-H "Content-Type: application/json" \
-d '{"name": "${{ github.ref_name }}", "group": "default", "seed": {"type": "database", "name": "production"}}' \
"https://api.turso.tech/v1/organizations/${{ secrets.ORG }}/databases"
# 256-bit key for AEGIS-256/AES-256
openssl rand -base64 32
# 128-bit key for AEGIS-128/AES-128
openssl rand -base64 16
turso db create secure-db \
--remote-encryption-key "YOUR_KEY" \
--remote-encryption-cipher aegis256
turso db shell secure-db --remote-encryption-key "YOUR_KEY"
| Cipher | Key Size | Recommendation |
|---|---|---|
aegis128l | 128-bit | Recommended for speed |
aegis256 | 256-bit | Recommended for security |
aes128gcm | 128-bit | NIST compliance |
aes256gcm | 256-bit | NIST compliance |
chacha20poly1305 | 256-bit | AES alternative |
| Extension | Description |
|---|---|
| JSON | JSON functions |
| FTS5 | Full-text search |
| R*Tree | Spatial indexing |
| SQLean Crypto | Hashing, encoding |
| SQLean Fuzzy | Fuzzy string matching |
| SQLean Math | Advanced math |
| SQLean Stats | Statistical functions |
| SQLean Text | String manipulation |
| SQLean UUID | UUID generation |
turso db create my-db --enable-extensions
| Mistake | Fix |
|---|---|
Using @libsql/client/web with file URLs | Use @libsql/client for local files |
| Long-running write transactions | Keep writes short, they block other writes |
| Opening local file during sync | Wait for sync to complete |
| Forgetting to sync embedded replicas | Call sync() or use syncInterval |
| Hardcoding credentials | Use environment variables |
| Not using transactions for related writes | Use batch() or transaction() |
| Creating vector index on wrong column type | Column must be vector type (F32_BLOB, etc.) |
batch() for multiple related operationsread transactions for read-only queries (parallel on replicas)syncInterval for embedded replicascompress_neighbors for large vector indexes