Nutze better-sqlite3 mit Prepared Statements, Transaktionen und einem eigenen Repository für schnelle und sichere Persistenz.
Diese Lektion in Kodokon öffnenSQLite läuft innerhalb deines Prozesses: kein Server, kein Netzwerk, eine Latenz pro Abfrage in der Größenordnung einer Mikrosekunde. Genau das macht die synchrone API von better-sqlite3 nicht nur akzeptabel, sondern oft schneller als einen asynchronen Treiber: Es lohnt sich nicht, für einen Lesevorgang, der kürzer als ein Tick ist, ein Promise und eine Runde der Event Loop zu bezahlen. Der Kompromiss ist beim Schreiben real: Eine langsame Abfrage blockiert die Loop. Halte deine Abfragen indiziert und kurz, dann bewältigt SQLite zehntausende Lesevorgänge pro Sekunde.
npm install better-sqlite3Beim Öffnen der Datenbank machen zwei Einstellungen den Unterschied. Der WAL-Modus (write-ahead logging) erlaubt Lesevorgänge während eines Schreibvorgangs, statt die gesamte Datei zu sperren. Und das Schema wird idempotent mit CREATE TABLE IF NOT EXISTS angewendet - völlig ausreichend, bevor du ein echtes Migrationswerkzeug einführst.
import Database from 'better-sqlite3';
export function createDb(path = 'app.db') {
const db = new Database(path);
db.pragma('journal_mode = WAL');
db.exec(`CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT NOT NULL UNIQUE,
name TEXT NOT NULL
)`);
return db;
}Die Datenzugriffsschicht - das Repository - ist das einzige Modul, das SQL spricht. Die Abfragen werden einmal bei der Konstruktion vorbereitet und dann bei jedem Aufruf wiederverwendet: Die Engine parst den SQL-Text nicht erneut, und die Werte laufen über Platzhalter ?, niemals über Verkettung. Es ist genau die Schnittstelle findAll, findByEmail, insert, die der Service aus der vorherigen Lektion erwartet.
export function createUserRepository(db) {
const insertStmt = db.prepare(
'INSERT INTO users (email, name) VALUES (?, ?)',
);
const byEmailStmt = db.prepare(
'SELECT * FROM users WHERE email = ?',
);
const allStmt = db.prepare(
'SELECT * FROM users ORDER BY id',
);
return {
insert(user) {
const info = insertStmt.run(user.email, user.name);
return { id: info.lastInsertRowid, ...user };
},
findByEmail(email) {
return byEmailStmt.get(email);
},
findAll() {
return allStmt.all();
},
};
}import { createDb } from './connection.js';
const db = createDb();
const insertStmt = db.prepare(
'INSERT INTO users (email, name) VALUES (?, ?)',
);
const insertMany = db.transaction((users) => {
for (const user of users) {
insertStmt.run(user.email, user.name);
}
});
insertMany([
{ email: 'ada@example.com', name: 'Ada' },
{ email: 'linus@example.com', name: 'Linus' },
]);? in einem Prepared Statement?db.transaction bei tausend Inserts?