Menu
Back to Blog
1 min read
Backend

Turso & libSQL: SQLite für die Edge

Turso als Edge-Hosted SQLite mit libSQL. Embedded Replicas, Vector Search und Local-First Development.

TursolibSQLSQLite EdgeEmbedded ReplicasLocal-FirstEdge Database
Turso & libSQL: SQLite für die Edge

Turso & libSQL: SQLite für die Edge

Meta-Description: Turso als Edge-Hosted SQLite mit libSQL. Embedded Replicas, Vector Search und Local-First Development.

Keywords: Turso, libSQL, SQLite Edge, Embedded Replicas, Local-First, Edge Database, Distributed SQLite


Einführung

Turso bringt SQLite in die Edge. Basierend auf libSQL (einem SQLite-Fork) bietet es embedded Replicas, globale Verteilung und native Vector Search – perfekt für Low-Latency Anwendungen weltweit.


Turso Architecture

┌─────────────────────────────────────────────────────────────┐
│                    TURSO ARCHITECTURE                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Primary Database (Write):                                  │
│  └── Zentraler Write-Node                                  │
│                                                             │
│  Edge Replicas (Read):                                      │
│  ├── Frankfurt                                             │
│  ├── New York                                              │
│  ├── Singapore                                             │
│  └── São Paulo                                             │
│      └── Automatische Synchronisation                      │
│                                                             │
│  Embedded Replicas (On-Device):                             │
│  ├── In-App SQLite Kopie                                   │
│  ├── Offline-fähig                                         │
│  └── Sync bei Reconnect                                    │
│                                                             │
│  Features:                                                  │
│  ├── libSQL (SQLite Fork)                                  │
│  ├── Native Vector Search                                  │
│  ├── Branching (wie Git)                                   │
│  └── MCP Server für AI Assistants                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Setup

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

# Login
turso auth login

# Database erstellen
turso db create my-app --location fra  # Frankfurt

# Replicas hinzufügen
turso db replicas add my-app --location iad  # US East
turso db replicas add my-app --location sin  # Singapore

# Connection URL und Token
turso db show my-app --url
turso db tokens create my-app
# Node.js Client installieren
npm install @libsql/client

Basic Connection

// lib/turso.ts
import { createClient } from '@libsql/client';

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

// Für lokale Entwicklung (SQLite File)
export const localDb = createClient({
  url: 'file:local.db'
});

CRUD Operations

import { turso } from '@/lib/turso';

// CREATE TABLE
async function initializeSchema() {
  await turso.execute(`
    CREATE TABLE IF NOT EXISTS users (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      email TEXT UNIQUE NOT NULL,
      name TEXT,
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP
    )
  `);

  await turso.execute(`
    CREATE TABLE IF NOT EXISTS posts (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      title TEXT NOT NULL,
      content TEXT,
      author_id INTEGER NOT NULL,
      published INTEGER DEFAULT 0,
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (author_id) REFERENCES users(id)
    )
  `);
}

// CREATE
async function createUser(email: string, name: string) {
  const result = await turso.execute({
    sql: 'INSERT INTO users (email, name) VALUES (?, ?) RETURNING *',
    args: [email, name]
  });
  return result.rows[0];
}

// READ
async function getUserById(id: number) {
  const result = await turso.execute({
    sql: 'SELECT * FROM users WHERE id = ?',
    args: [id]
  });
  return result.rows[0];
}

async function getPostsWithAuthors() {
  const result = await turso.execute(`
    SELECT
      p.id,
      p.title,
      p.content,
      p.created_at,
      u.name as author_name,
      u.email as author_email
    FROM posts p
    JOIN users u ON p.author_id = u.id
    WHERE p.published = 1
    ORDER BY p.created_at DESC
  `);
  return result.rows;
}

// UPDATE
async function updatePost(id: number, title: string, content: string) {
  const result = await turso.execute({
    sql: 'UPDATE posts SET title = ?, content = ? WHERE id = ? RETURNING *',
    args: [title, content, id]
  });
  return result.rows[0];
}

// DELETE
async function deletePost(id: number) {
  await turso.execute({
    sql: 'DELETE FROM posts WHERE id = ?',
    args: [id]
  });
}

// BATCH (Transaction)
async function createUserWithPosts(
  user: { email: string; name: string },
  posts: { title: string; content: string }[]
) {
  const result = await turso.batch([
    {
      sql: 'INSERT INTO users (email, name) VALUES (?, ?) RETURNING id',
      args: [user.email, user.name]
    },
    ...posts.map(post => ({
      sql: 'INSERT INTO posts (title, content, author_id) VALUES (?, ?, last_insert_rowid())',
      args: [post.title, post.content]
    }))
  ], 'write');

  return result;
}

Embedded Replicas (Local-First)

// lib/turso-embedded.ts
import { createClient } from '@libsql/client';

// Embedded Replica mit Sync
export const db = createClient({
  url: 'file:local-replica.db',  // Lokale SQLite Datei
  syncUrl: process.env.TURSO_DATABASE_URL!,
  authToken: process.env.TURSO_AUTH_TOKEN!,
  syncInterval: 60  // Sync alle 60 Sekunden
});

// Manuelle Synchronisation
async function syncDatabase() {
  await db.sync();
  console.log('Database synced with remote');
}

// Verwendung
async function getDataWithFallback() {
  try {
    // Versuche lokale Query (schnell!)
    const result = await db.execute('SELECT * FROM posts LIMIT 10');
    return result.rows;
  } catch (error) {
    // Fallback zu Remote bei Fehler
    await db.sync();
    const result = await db.execute('SELECT * FROM posts LIMIT 10');
    return result.rows;
  }
}

Vector Search

// libSQL unterstützt native Vector Operations

// Tabelle mit Vector Column
await turso.execute(`
  CREATE TABLE IF NOT EXISTS documents (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    content TEXT NOT NULL,
    embedding F32_BLOB(1536),  -- OpenAI ada-002 Dimension
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
  )
`);

// Vector Index erstellen
await turso.execute(`
  CREATE INDEX IF NOT EXISTS documents_embedding_idx
  ON documents (libsql_vector_idx(embedding))
`);

// Document mit Embedding speichern
async function saveDocument(content: string, embedding: number[]) {
  await turso.execute({
    sql: `
      INSERT INTO documents (content, embedding)
      VALUES (?, vector32(?))
    `,
    args: [content, JSON.stringify(embedding)]
  });
}

// Similarity Search
async function searchSimilar(queryEmbedding: number[], limit: number = 5) {
  const result = await turso.execute({
    sql: `
      SELECT
        id,
        content,
        vector_distance_cos(embedding, vector32(?)) as distance
      FROM documents
      ORDER BY distance ASC
      LIMIT ?
    `,
    args: [JSON.stringify(queryEmbedding), limit]
  });

  return result.rows.map(row => ({
    id: row.id,
    content: row.content,
    similarity: 1 - (row.distance as number)  // Convert distance to similarity
  }));
}

Drizzle ORM Integration

// drizzle.config.ts
import { defineConfig } from 'drizzle-kit';

export default defineConfig({
  schema: './src/db/schema.ts',
  out: './drizzle',
  dialect: 'turso',
  dbCredentials: {
    url: process.env.TURSO_DATABASE_URL!,
    authToken: process.env.TURSO_AUTH_TOKEN!
  }
});
// src/db/schema.ts
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';

export const users = sqliteTable('users', {
  id: integer('id').primaryKey({ autoIncrement: true }),
  email: text('email').notNull().unique(),
  name: text('name'),
  createdAt: integer('created_at', { mode: 'timestamp' })
    .notNull()
    .$defaultFn(() => new Date())
});

export const posts = sqliteTable('posts', {
  id: integer('id').primaryKey({ autoIncrement: true }),
  title: text('title').notNull(),
  content: text('content'),
  authorId: integer('author_id')
    .notNull()
    .references(() => users.id),
  published: integer('published', { mode: 'boolean' }).default(false),
  createdAt: integer('created_at', { mode: 'timestamp' })
    .notNull()
    .$defaultFn(() => new Date())
});
// src/db/index.ts
import { drizzle } from 'drizzle-orm/libsql';
import { createClient } from '@libsql/client';
import * as schema from './schema';

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

export const db = drizzle(client, { schema });

// Queries
import { eq } from 'drizzle-orm';

async function getUserWithPosts(userId: number) {
  return await db.query.users.findFirst({
    where: eq(users.id, userId),
    with: {
      posts: true
    }
  });
}

Database Branching

# Branch erstellen (wie Git)
turso db branch create my-app feature-branch

# Branch verwenden
turso db show my-app/feature-branch --url

# Branch mergen (manuell - Schema migrieren)
turso db branch delete my-app feature-branch
// Branch in Code verwenden
const branchDb = createClient({
  url: process.env.TURSO_BRANCH_URL!,  // Feature Branch URL
  authToken: process.env.TURSO_AUTH_TOKEN!
});

// Schema-Änderungen testen
await branchDb.execute(`
  ALTER TABLE users ADD COLUMN avatar_url TEXT
`);

// Nach Test: Änderungen auf Production anwenden

Edge Functions Integration

// Cloudflare Workers
export default {
  async fetch(request: Request, env: Env) {
    const db = createClient({
      url: env.TURSO_DATABASE_URL,
      authToken: env.TURSO_AUTH_TOKEN
    });

    const { pathname } = new URL(request.url);

    if (pathname === '/api/posts') {
      const result = await db.execute(
        'SELECT * FROM posts WHERE published = 1 ORDER BY created_at DESC LIMIT 10'
      );

      return new Response(JSON.stringify(result.rows), {
        headers: { 'Content-Type': 'application/json' }
      });
    }

    return new Response('Not Found', { status: 404 });
  }
};

Fazit

Turso bietet:

  1. Global Edge Distribution: Replicas weltweit für Low-Latency
  2. Embedded Replicas: Local-First mit automatischem Sync
  3. SQLite Compatibility: Bewährte Technologie, moderne Distribution
  4. Native Vector Search: AI-ready ohne externe Services

Ideal für globale Anwendungen mit Offline-Support.


Bildprompts

  1. "Globe with database nodes connected at edge locations, global distribution"
  2. "SQLite file syncing between device and cloud, embedded replica concept"
  3. "Local-first application working offline then syncing, connectivity visualization"

Quellen