Kodokon kodokon.com

Persistance avec SQLite : la couche d'accès aux données

Exploitez better-sqlite3 avec requêtes préparées, transactions et dépôt dédié pour une persistance rapide et sûre.

9 min · 3 questions

Ouvrir cette leçon dans Kodokon

SQLite tourne dans votre processus : pas de serveur, pas de réseau, une latence par requête de l'ordre de la microseconde. C'est ce qui rend l'API synchrone de better-sqlite3 non seulement acceptable mais souvent plus rapide qu'un pilote asynchrone : inutile de payer une promesse et un tour de boucle d'événements pour une lecture plus courte qu'un tick. Le compromis est réel en écriture : une requête lente bloque la boucle. Gardez vos requêtes indexées et courtes, et SQLite encaisse des dizaines de milliers de lectures par seconde.

BASH
npm install better-sqlite3
Module natif : il compile à l'installation si besoin.

À l'ouverture, deux réglages font la différence. Le mode WAL (write-ahead logging) autorise les lectures pendant une écriture au lieu de verrouiller tout le fichier. Et le schéma s'applique de façon idempotente avec CREATE TABLE IF NOT EXISTS - largement suffisant avant d'introduire un véritable outil de migration.

JAVASCRIPT
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;
}
connection.js : ouverture, réglages, schéma idempotent.

La couche d'accès aux données - le dépôt - est le seul module qui parle SQL. Les requêtes sont préparées une seule fois à la construction puis réutilisées à chaque appel : le moteur ne ré-analyse pas le texte SQL, et les valeurs passent par des marqueurs ?, jamais par concaténation. C'est exactement l'interface findAll, findByEmail, insert que le service de la leçon précédente attend.

JAVASCRIPT
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();
    },
  };
}
Préparées une fois, exécutées mille fois : run, get, all.
JAVASCRIPT
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' },
]);
db.transaction : tout passe, ou tout est annulé.

Quiz de validation

Vérifiez que vous avez bien retenu les points clés de cette leçon.

  1. Pourquoi l'API synchrone de better-sqlite3 n'est-elle pas un contresens en Node ?
    • Parce que Node exécute les modules natifs dans un thread séparé
    • Parce que SQLite est en process : la requête coûte moins cher que l'orchestration asynchrone qu'elle éviterait
    • Parce que les écritures sont mises en file automatiquement
    • Parce que le mode WAL rend toutes les requêtes non bloquantes
  2. Que garantissent les marqueurs ? d'une requête préparée ?
    • Les valeurs sont transmises séparément du SQL et ne peuvent jamais être exécutées comme du code
    • Les valeurs sont chiffrées avant d'être stockées
    • La requête est automatiquement mise en cache disque
  3. Quel double bénéfice apporte db.transaction pour mille insertions ?
    • Compression des données et déduplication automatique
    • Atomicité (tout ou rien) et une seule synchronisation disque au lieu de mille
    • Validation du schéma et création des index manquants