Add blog posts, cleanup unused files, update components
- Add 100 blog posts covering AI, development, and tech topics - Add .env.example for environment configuration - Add accessibility and lighthouse audit scripts - Remove obsolete SEO reports and temporary files - Remove dev-dist build artifacts and backup files - Remove unused portrait images (moved/consolidated elsewhere) - Update contact form and component improvements Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,450 @@
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
```
|
||||
|
||||
```bash
|
||||
# Node.js Client installieren
|
||||
npm install @libsql/client
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Basic Connection
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
```typescript
|
||||
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)
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
```typescript
|
||||
// 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!
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
```typescript
|
||||
// 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())
|
||||
});
|
||||
```
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
```
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
- [Turso Documentation](https://docs.turso.tech/)
|
||||
- [libSQL GitHub](https://github.com/tursodatabase/libsql)
|
||||
- [Turso Embedded Replicas](https://turso.tech/blog/local-first-cloud-connected-sqlite-with-turso-embedded-replicas)
|
||||
- [Turso Vector Search](https://docs.turso.tech/features/vector-search)
|
||||
Reference in New Issue
Block a user