Claude Code for Turso SQLite Edge Database Tutorial
Turso provides a distributed SQLite database optimized for edge computing When combined with Claude Code, you get an AI-assisted workflow for building applications that need low-latency data access worldwide. This tutorial covers setting up Turso, connecting it to your project, and using Claude’s capabilities to accelerate development.
Why Turso for Edge Computing?
Edge computing demands databases that are lightweight, fast to start, and capable of running close to users worldwide. Turso is libSQL, an open-source fork of SQLite designed specifically for edge computing and distributed databases. Unlike traditional SQLite, Turso offers replication, edge deployment capabilities, and a cloud-managed platform while maintaining SQLite’s simplicity. It addresses edge needs through:
- Embedded execution: The libSQL library runs directly in your application process, eliminating network latency
- Edge replicas: Deploy database replicas to hundreds of edge locations globally
- HTTP client: Query Turso over HTTP without maintaining persistent connections
- Row-level replication: Replicate only the data each edge location needs
Setting Up Turso SQLite
Before integrating with Claude Code, you need a Turso database instance. Install the Turso CLI and create your first database:
# Install Turso CLI
curl -sSfL https://get.tur.so/install.sh | bash
# Authenticate
turso auth signup
# Create a new database
turso db create my-edge-app
# Get connection string
turso db show my-edge-app --url
The connection string follows the libSQL format: libsql://your-database.turso.io. Turso offers both HTTP and WebSocket connections, with the latter providing better performance for interactive applications.
Connecting to Your Project
Most Node.js projects use the libsql client library. Initialize the connection in your project:
import { createClient } from "@libsql/client";
const client = createClient({
url: process.env.TURSO_DATABASE_URL,
authToken: process.env.TURSO_AUTH_TOKEN,
});
// Test the connection
const result = await client.execute("SELECT 1");
console.log("Connected:", result);
For TypeScript projects, add type definitions:
npm install @libsql/client @types/node
Claude Code can help you generate proper type definitions for your tables. Describe your data model and ask Claude to write the schema and TypeScript interfaces.
Schema Design with Claude
When designing your database schema, use Claude’s skills for structured thinking. The /tdd skill proves particularly useful here—you can define your data requirements first, then generate migrations that satisfy those requirements.
Suppose you need a user authentication system:
/tdd
Design a SQLite schema for users, sessions, and API keys.
- Users need: id, email, password_hash, created_at, updated_at
- Sessions need: id, user_id, token, expires_at
- API keys need: id, user_id, key_hash, name, last_used_at
Include foreign keys and appropriate indexes.
Claude generates the migration:
CREATE TABLE users (
id TEXT PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE sessions (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
token TEXT UNIQUE NOT NULL,
expires_at TEXT NOT NULL
);
CREATE INDEX idx_sessions_user_id ON sessions(user_id);
CREATE INDEX idx_sessions_token ON sessions(token);
CREATE TABLE api_keys (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
key_hash TEXT NOT NULL,
name TEXT NOT NULL,
last_used_at TEXT
);
CREATE INDEX idx_api_keys_user_id ON api_keys(user_id);
Query Building Patterns
Turso works well with ORMs like Drizzle or Prisma. Drizzle provides particularly lightweight SQL generation that pairs well with edge deployments. Here’s how to set up Drizzle with Turso:
npm install drizzle-kit drizzle-orm @libsql/client
Define your schema in src/db/schema.ts:
import { sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { sql } from 'drizzle-orm';
export const users = sqliteTable('users', {
id: text('id').primaryKey(),
email: text('email').unique().notNull(),
passwordHash: text('password_hash').notNull(),
createdAt: text('created_at').default(sql`datetime('now')`),
updatedAt: text('updated_at').default(sql`datetime('now')`),
});
export const sessions = sqliteTable('sessions', {
id: text('id').primaryKey(),
userId: text('user_id').references(() => users.id).notNull(),
token: text('token').unique().notNull(),
expiresAt: text('expires_at').notNull(),
});
Generate migrations with Drizzle Kit:
npx drizzle-kit generate:sqlite
npx drizzle-kit push:sqlite
Python Client with Async Support
For Python projects, Claude Code can generate an async-ready Turso client with proper error handling:
import libsql_client
from libsql_client import ResultSet
import asyncio
from typing import Optional
class TursoDB:
def __init__(self, database_url: str, auth_token: Optional[str] = None):
self.database_url = database_url
self.auth_token = auth_token
self._client: Optional[libsql_client.Client] = None
async def connect(self):
self._client = await libsql_client.connect(
url=self.database_url,
auth_token=self.auth_token
)
async def execute(self, query: str, parameters: list = None) -> ResultSet:
if not self._client:
await self.connect()
return await self._client.execute(query, parameters or [])
async def close(self):
if self._client:
await self._client.close()
With this client, Claude Code can generate parameterized CRUD functions for any table. For example, an upsert-on-conflict pattern for a user preferences table:
async def insert_user_preference(db: TursoDB, user_id: str, key: str, value: str, edge_location: str) -> dict:
result = await db.execute(
"""INSERT INTO user_preferences (user_id, preference_key, preference_value, edge_location)
VALUES (?, ?, ?, ?)
ON CONFLICT(user_id, preference_key, edge_location)
DO UPDATE SET preference_value = excluded.preference_value,
updated_at = strftime('%s', 'now')""",
[user_id, key, value, edge_location]
)
return {"success": True, "last_insert_rowid": result.last_insert_rowid}
Edge Replication Patterns
One of Turso’s strengths is its ability to replicate data to edge locations. Claude Code can help you design effective replication strategies.
Edge-First Writes
For applications that write at the edge and sync later, Claude Code can scaffold an EdgeFirstWriter class that writes to local replicas first, then syncs to the primary:
from datetime import datetime
class EdgeFirstWriter:
def __init__(self, edge_db: TursoDB, primary_db: TursoDB):
self.edge_db = edge_db
self.primary_db = primary_db
async def write_local(self, table: str, data: dict) -> dict:
data['_edge_written_at'] = int(datetime.now().timestamp())
data['_synced'] = 0
columns = ', '.join(data.keys())
placeholders = ', '.join(['?' for _ in data])
await self.edge_db.execute(
f"INSERT INTO {table} ({columns}) VALUES ({placeholders})",
list(data.values())
)
return {"status": "local_write", "sync_pending": True}
async def sync_to_primary(self, table: str, local_id: int) -> dict:
result = await self.edge_db.execute(
f"SELECT * FROM {table} WHERE id = ? AND _synced = 0",
[local_id]
)
if not result.rows:
return {"status": "already_synced"}
record = dict(result.rows[0])
del record['id']
await self.primary_db.execute(
f"INSERT INTO {table} ({', '.join(record.keys())}) VALUES ({', '.join(['?' for _ in record])})",
list(record.values())
)
await self.edge_db.execute(
f"UPDATE {table} SET _synced = 1 WHERE id = ?", [local_id]
)
return {"status": "synced", "table": table, "local_id": local_id}
Handling Sync Conflicts
When multiple edge locations update the same record, a last-write-wins resolver using updated_at timestamps keeps data consistent:
async def resolve_conflict(edge_db: TursoDB, user_id: str, preference_key: str) -> dict:
result = await edge_db.execute(
"""SELECT * FROM user_preferences
WHERE user_id = ? AND preference_key = ?
ORDER BY updated_at DESC""",
[user_id, preference_key]
)
versions = [dict(row) for row in result.rows]
if len(versions) <= 1:
return {"resolved": True, "chosen_version": versions[0] if versions else None}
winner = versions[0]
await edge_db.execute(
"""DELETE FROM user_preferences
WHERE user_id = ? AND preference_key = ? AND id != ?""",
[user_id, preference_key, winner['id']]
)
return {
"resolved": True,
"chosen_version": winner,
"conflicts_resolved": len(versions) - 1
}
Edge Function Integration
Deploying to edge runtimes like Cloudflare Workers or Vercel Edge requires specific handling. Turso’s HTTP endpoint works with these environments:
// workers/my-worker.ts
import { createClient } from "@libsql/client/web";
export default {
async fetch(request: Request): Promise<Response> {
const client = createClient({
url: TURSO_URL,
authToken: TURSO_TOKEN,
});
const users = await client.execute(
"SELECT id, email FROM users LIMIT 10"
);
return new Response(JSON.stringify(users.rows), {
headers: { "Content-Type": "application/json" },
});
},
};
The web client uses fetch internally, making it compatible with any environment that supports the Web Fetch API.
Using Claude Skills for Database Tasks
Several Claude skills enhance database development workflows:
- tdd: Write tests before implementing database functions
- supermemory: Remember complex schema relationships across sessions
- code-review: Review migrations and query performance
- devops: Configure deployment pipelines with database migrations
The supermemory skill stores schema documentation and business rules, making it easier to maintain consistency as your application evolves.
Performance Considerations
Turso’s edge replication significantly reduces latency, but you should still optimize query patterns:
-- Use covering indexes for frequent reads
CREATE INDEX idx_users_email_covering
ON users(email) INCLUDE (id, password_hash);
-- Batch inserts for bulk operations
INSERT INTO users (id, email, password_hash) VALUES
(?, ?, ?), (?, ?, ?), (?, ?, ?);
Monitor query performance using Turso’s dashboard or add logging:
const start = Date.now();
const result = await client.execute(query);
console.log(`Query took ${Date.now() - start}ms`);
Best Practices for Claude Code + Turso
When working with Turso and Claude Code, keep these practices in mind:
- Use parameterized queries: Always use
?placeholders instead of string interpolation to prevent SQL injection - Batch operations: Use bulk inserts when possible to reduce round trips
- Index strategically: Create indexes on columns used in WHERE clauses and covering indexes for frequent reads
- Handle connection pooling: Reuse connections when possible for better performance
- Implement retry logic: Network requests to edge locations can fail; handle transient errors gracefully
- Monitor query performance: Log query durations and use Turso’s dashboard to spot slow queries early
Next Steps
With your Turso database connected to Claude Code, you have a powerful setup for building edge applications. The AI-assisted workflow handles everything from schema design to migration management, letting you focus on application logic.
Explore embedding Turso in serverless functions, implementing real-time subscriptions with WebSockets, or adding row-level security for multi-tenant applications. Claude Code’s skills like pdf can help generate documentation for your data layer, while frontend-design skills ensure your application UI properly integrates with your backend.
Related Reading
- Automated Testing Pipeline with Claude TDD Skill
- Claude SuperMemory Skill: Persistent Context Guide
- Claude Code Skills for Supabase Full Stack Apps
- Integrations Hub
Built by theluckystrike — More at zovo.one