Claude Code SQLite: Datenbankentwicklung für lokale Apps mit KI
SQLite ist die am häufigsten eingesetzte Datenbank der Welt — in Browsern, Mobilgeräten, Desktop-Apps, Embedded-Systemen und Testumgebungen. Trotzdem wirkt der Einstieg oft komplizierter als nötig: Welche Node.js-Bibliothek für welchen Use Case? Wie strukturiert man Migrations sauber? Wann lohnt sich WAL-Modus? Claude Code beantwortet diese Fragen nicht abstrakt, sondern direkt im Kontext deines Projekts.
Dieser Artikel zeigt, wie du SQLite-Datenbankentwicklung mit Claude Code beschleunigst — von der ersten Tabelle bis zu automatisch generierten Migrations und Performance-Tuning. Die Beispiele sind praxisnah: better-sqlite3, node:sqlite (Node.js 22+), Drizzle ORM und rohe SQL-Skripte.
Claude Code Mastery — Datenbankentwicklung, Agents, Hooks auf Deutsch
SQLite ist ein Kapitel. Im Kurs lernst du den vollständigen Workflow: Schema-Design, Migrations, Testing, Agents und produktive Claude Code-Workflows auf Deutsch. Einmalig bezahlt, kein Abo.
Zum Kurs — Jetzt starten → Einmalzahlung · Kein Abo · 14 Tage Rückgaberecht1. Wann SQLite die richtige Wahl ist
SQLite ist keine "kleine" Datenbank — es ist eine andere Kategorie. Kein Server, keine Verbindungen, keine Konfiguration. Die Datenbank ist eine einzige Datei, die deine Anwendung direkt liest und schreibt. Das macht SQLite ideal für:
- Electron-Apps und Desktop-Software: lokale Datenpersistenz ohne externen Datenbankserver
- Embedded-Systeme und IoT: wenig Ressourcen, kein Netzwerk
- CLI-Tools und Skripte: strukturierter State ohne JSON-Datei-Chaos
- Testumgebungen: In-Memory-Datenbank für schnelle, isolierte Tests
- Prototypen und MVPs: zero Setup, sofort loslegen
Für Multi-User-Webanwendungen mit hoher Schreiblast ist PostgreSQL oder MySQL die bessere Wahl. SQLite glänzt, wenn die Datenbank nah an der Anwendung lebt — auf demselben Rechner, im selben Prozess.
SQLite vs. PostgreSQL: Die Grenze liegt bei gleichzeitigen Schreibzugriffen. SQLite sperrt die gesamte Datei beim Schreiben. Mit WAL-Modus (Write-Ahead Logging) können mehrere Leser gleichzeitig lesen, während ein Schreiber aktiv ist — das löst die meisten Parallelitätsprobleme für typische Desktop-App-Szenarien.
2. Setup: better-sqlite3 vs. node:sqlite
In Node.js gibt es zwei seriöse Optionen für SQLite. Claude Code kennt beide und wählt die passende für deinen Context:
better-sqlite3 (etabliert, synchron)
npm install better-sqlite3
npm install -D @types/better-sqlite3
import Database from 'better-sqlite3';
const db = new Database('app.db');
db.pragma('journal_mode = WAL');
db.pragma('foreign_keys = ON');
const createUsers = db.prepare(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
created_at INTEGER NOT NULL DEFAULT (unixepoch())
)
`);
createUsers.run();
better-sqlite3 ist synchron — kein await, kein Callback-Hell. Für Electron-Apps und CLI-Tools ist das oft genau das Richtige: einfacher, linearer Code ohne asynchrone Komplexität.
node:sqlite (Node.js 22+, nativ)
import { DatabaseSync } from 'node:sqlite';
const db = new DatabaseSync('app.db');
db.exec(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
created_at INTEGER NOT NULL DEFAULT (unixepoch())
)
`);
Das native Modul braucht keine externe Dependency. Für neue Projekte auf Node.js 22+ ist es eine gute Wahl, wenn du Abhängigkeiten minimieren willst. Die API ist ähnlich zu better-sqlite3, aber noch nicht feature-vollständig.
3. Prepared Statements und Transaktionen
Rohe String-Interpolation in SQL-Queries ist eine der häufigsten Sicherheitslücken. Prepared Statements lösen das — und sind gleichzeitig schneller, weil die Query einmal kompiliert und mehrfach ausgeführt wird.
const insertUser = db.prepare(
'INSERT INTO users (email, name) VALUES (?, ?)'
);
const getUserByEmail = db.prepare(
'SELECT * FROM users WHERE email = ?'
);
const updateName = db.prepare(
'UPDATE users SET name = ? WHERE id = ?'
);
insertUser.run('alice@example.com', 'Alice');
const user = getUserByEmail.get('alice@example.com');
updateName.run('Alice Smith', user.id);
Für mehrere zusammenhängende Schreiboperationen sind Transaktionen entscheidend — entweder alles schlägt fehl, oder alles gelingt:
const transferCredits = db.transaction((fromId, toId, amount) => {
const debit = db.prepare(
'UPDATE accounts SET credits = credits - ? WHERE id = ? AND credits >= ?'
);
const credit = db.prepare(
'UPDATE accounts SET credits = credits + ? WHERE id = ?'
);
const result = debit.run(amount, fromId, amount);
if (result.changes === 0) {
throw new Error('Insufficient credits');
}
credit.run(amount, toId);
});
transferCredits(userId1, userId2, 100);
Claude Code schreibt Transaktionen wie diese auf Anfrage — beschreibe einfach die Geschäftslogik, und es generiert den korrekten Code inklusive Fehlerbehandlung.
4. WAL-Modus und Performance-Tuning
Der wichtigste Performance-Schalter für SQLite ist der WAL-Modus (Write-Ahead Logging). Er trennt Lese- und Schreiboperationen, sodass Leser nicht auf laufende Schreiber warten müssen.
db.pragma('journal_mode = WAL');
db.pragma('synchronous = NORMAL');
db.pragma('cache_size = -64000');
db.pragma('temp_store = MEMORY');
db.pragma('mmap_size = 268435456');
Diese Pragmas solltest du einmalig beim Öffnen der Datenbank setzen. Claude Code erklärt auf Anfrage, welche Werte für deinen Use Case sinnvoll sind — eine Electron-App mit häufigen kleinen Schreibvorgängen hat andere Anforderungen als ein CLI-Tool das selten, aber groß schreibt.
Indexe nicht vergessen: WAL-Modus hilft bei Parallelität, aber für Query-Performance sind Indexe entscheidend. Claude Code analysiert deine häufigen Queries und schlägt passende Indexe vor — frag einfach: "Welche Indexe brauche ich für diese Queries?"
5. In-Memory-Datenbank für Tests
Einer der größten Vorteile von SQLite im Test-Setup: die In-Memory-Datenbank. Kein Aufräumen zwischen Tests, keine Testdaten die sich anhäufen, kein Netzwerk. Jeder Test bekommt eine frische, leere Datenbank.
import Database from 'better-sqlite3';
import { describe, it, beforeEach, expect } from 'vitest';
function createTestDb() {
const db = new Database(':memory:');
db.pragma('foreign_keys = ON');
db.exec(`
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT NOT NULL UNIQUE,
name TEXT NOT NULL
);
CREATE TABLE posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id),
title TEXT NOT NULL,
body TEXT NOT NULL
);
`);
return db;
}
describe('UserRepository', () => {
let db: Database.Database;
beforeEach(() => {
db = createTestDb();
});
it('should create a user', () => {
const insert = db.prepare('INSERT INTO users (email, name) VALUES (?, ?)');
insert.run('test@example.com', 'Test User');
const user = db.prepare('SELECT * FROM users WHERE email = ?')
.get('test@example.com') as { id: number; email: string; name: string };
expect(user.email).toBe('test@example.com');
expect(user.name).toBe('Test User');
});
});
Mit ':memory:' als Datenbankpfad lebt die Datenbank nur im RAM. Tests laufen schnell — typisch unter 10 ms pro Test, auch wenn das Schema komplex ist.
6. Migrations mit Drizzle ORM oder raw SQL
Schemaänderungen ohne Migration-System enden im Chaos. SQLite hat keine ALTER COLUMN-Unterstützung — Spalten ändern bedeutet eine neue Tabelle erstellen, Daten kopieren, alte Tabelle löschen. Migrations halten diesen Prozess nachvollziehbar.
Drizzle ORM (empfohlen für TypeScript-Projekte)
npm install drizzle-orm better-sqlite3
npm install -D drizzle-kit @types/better-sqlite3
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').notNull(),
createdAt: integer('created_at', { mode: 'timestamp' })
.$defaultFn(() => new Date()),
});
export const posts = sqliteTable('posts', {
id: integer('id').primaryKey({ autoIncrement: true }),
userId: integer('user_id').notNull().references(() => users.id),
title: text('title').notNull(),
body: text('body').notNull(),
});
npx drizzle-kit generate
npx drizzle-kit migrate
Claude Code kann das komplette Schema aus einer Beschreibung generieren: "Ich brauche eine Tabelle für Benutzer mit E-Mail, Name und Erstellungsdatum, verknüpft mit Posts die einen Titel und einen Body haben." Ergebnis: vollständiges Drizzle-Schema plus Migration.
Raw SQL Migrations (ohne ORM)
import Database from 'better-sqlite3';
import { readdirSync, readFileSync } from 'fs';
import { join } from 'path';
function runMigrations(db: Database.Database, migrationsDir: string) {
db.exec(`
CREATE TABLE IF NOT EXISTS migrations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
filename TEXT NOT NULL UNIQUE,
applied_at INTEGER NOT NULL DEFAULT (unixepoch())
)
`);
const applied = new Set(
(db.prepare('SELECT filename FROM migrations').all() as { filename: string }[])
.map(r => r.filename)
);
const files = readdirSync(migrationsDir)
.filter(f => f.endsWith('.sql'))
.sort();
const applyMigration = db.transaction((filename: string, sql: string) => {
db.exec(sql);
db.prepare('INSERT INTO migrations (filename) VALUES (?)').run(filename);
});
for (const file of files) {
if (!applied.has(file)) {
const sql = readFileSync(join(migrationsDir, file), 'utf-8');
applyMigration(file, sql);
console.log(`Applied migration: ${file}`);
}
}
}
const db = new Database('app.db');
runMigrations(db, './migrations');
Dieser Ansatz braucht kein ORM-Framework. Migration-Dateien heißen 001_create_users.sql, 002_add_posts.sql — die Reihenfolge ist durch den Dateinamen garantiert.
7. Wie Claude Code die SQLite-Entwicklung beschleunigt
Claude Code ist kein SQL-Generator, der blind Code produziert — es liest deinen bestehenden Code, versteht das Schema und macht konkrete Vorschläge die in deinen Kontext passen.
Schema-Design: Beschreibe deine Entitäten und deren Beziehungen in normaler Sprache. Claude Code entwirft das Schema mit korrekten Foreign Keys, sinnvollen Datentypen und notwendigen Indexen — und erklärt die Entscheidungen.
claude "Entwirf ein SQLite-Schema für eine To-Do-App mit Benutzern,
Listen und Aufgaben. Aufgaben gehören zu Listen, Listen gehören zu Benutzern.
Aufgaben haben einen Fälligkeitstermin und einen Erledigt-Status."
Query-Optimierung: Slow Queries finden ist mühsam. Übergib die Query und das Schema, und Claude Code analysiert den Ausführungsplan und schlägt Indexe oder Query-Umstrukturierungen vor.
claude "Diese Query ist langsam auf 100.000 Zeilen. Analysiere den
EXPLAIN QUERY PLAN und schlage Optimierungen vor:
SELECT u.name, COUNT(p.id) as post_count
FROM users u LEFT JOIN posts p ON p.user_id = u.id
WHERE u.created_at > 1700000000
GROUP BY u.id
ORDER BY post_count DESC"
Migration-Generierung: Schemaänderungen formulieren — Claude Code schreibt die Migration inklusive dem SQLite-üblichen Tanz für Column-Änderungen (neue Tabelle, Daten kopieren, umbenennen).
- Schema aus Beschreibung generieren — spart 30 bis 60 Minuten Designarbeit
- Migrations schreiben inkl. Rollback — besonders wertvoll für Column-Renames in SQLite
- Performance-Probleme diagnostizieren — EXPLAIN QUERY PLAN verstehen ohne Dokumentation
- Test-Fixtures generieren — realistische Testdaten für komplexe Schemas
Zwei verwandte Artikel die auf diesem Thema aufbauen:
- Claude Code Drizzle ORM — vollständiger Drizzle-Workflow mit Migrations und Type Safety
- Claude Code PostgreSQL — wenn SQLite zu klein wird und der Wechsel ansteht
Claude Code Mastery — von SQLite bis zum produktiven Agenten
Datenbankentwicklung ist eine Stärke von Claude Code — aber nicht die einzige. Im Kurs lernst du Agents, MCP-Server, Hooks, Multi-Agent-Workflows und mehr. Vollständig auf Deutsch, einmalig bezahlt.
Jetzt starten → Einmalzahlung · Kein Abo · 14 Tage RückgaberechtKurs · Claude Code Mastery
Von SQLite bis zum produktiven AI-Agenten
Datenbanken. Agents. MCP. Hooks. Multi-Agent-Workflows. Alles auf Deutsch, einmalig bezahlt — kein Abo, keine Plattformabhängigkeit.
Einmalzahlung · Kein Abo · 14 Tage Rückgaberecht