Aller au contenu principal

L'alternative à @capacitor-community/sqlite en 2026

Vous cherchez une alternative à @capacitor-community/sqlite ? Comparez-l’à Capgo Fast SQL et migrez étape par étape votre code SQLite Capacitor et vos données utilisateur.

logo Capgo pour un guide sur les alternatives au plugin SQLite de la communauté Capacitor

La principale alternative à @capacitor-community/sqlite sur Capacitor 8 est @capgo/capacitor-fast-sql: un plugin SQLite natif pour iOS, Android et le web qui garde SQLCipher optionnel et envoie les données de requêtes via un canal HTTP local plutôt que le pont Capacitor. Ce guide compare honnêtement les deux, indique quand rester sur le plugin communautaire et détaille la migration de votre code et des bases de données existantes de vos utilisateurs.

Pourquoi les équipes cherchent une alternative

@capacitor-community/sqlite est le plugin SQLite par défaut pour Capacitor depuis des années et il est toujours maintenu (la version 8.1.1 a été publiée en août 2026). Les raisons qui poussent à changer sont précises :

  1. Sérialisation du bridge. Chaque résultat passe par le bridge Capacitor sous forme de JSON. C’est acceptable pour de petites requêtes, mais cela se fait sentir quand vous chargez des milliers de lignes pour la synchronisation ou la recherche.
  2. SQLCipher est toujours inclus. Le plugin dépend de SQLCipher sur iOS et Android, même si vous ne chiffrez jamais. Cela augmente la taille native et signifie que votre binaire contient du code de chiffrement que vous devez prendre en compte dans vos réponses de conformité à l’exportation.
  3. Modèle de stockage web. Sur le web, il utilise le jeep-sqlite composant : sql.js en mémoire, persisté dans IndexedDB lorsque vous appelez saveToStore(). Vous devez penser à sauvegarder, et toute la base de données reste en mémoire.
  4. Une surface API étendue. Wrappers de connexion, contrôles de cohérence, connexions en lecture seule et non conformes, synchronisation de tables, import et export JSON. Puissant, mais beaucoup à apprendre pour « exécuter du SQL ».
  5. Une seule phrase secrète par application. Le chiffrement utilise un secret unique stocké par le plugin (setEncryptionSecret), partagé par toutes les bases de données chiffrées de l’application.

Comparaison côte à côte

@capacitor-community/sqlite 8.1 @capgo/capacitor-fast-sql 8.2
Plateformes iOS, Android, Web, Electron iOS, Android, Web
Data path bridge Capacitor Serveur HTTP local avec un token par connexion (natif)
Moteur web jeep-sqlite (sql.js + IndexedDB, manuel) saveToStore) Officiel @sqlite.org/sqlite-wasm avec OPFS, les écritures se font dans le fichier
Chiffrement SQLCipher toujours inclus, une seule passphrase gérée par le plugin SQLCipher optionnel, clé passée par base à l'ouverture connect()
Swift Package Manager Oui (Package.swift) Oui (Package.swift)
Versionnage du schéma upgradeStatements / addUpgradeStatement Le vôtre, généralement PRAGMA user_version
Import/export JSON, sync des tables Oui Non
Base de données pré-remplie depuis les assets copyFromAssets() Copiez le fichier vous-même avant connect()
Assistant clé-valeur Non KeyValueStore
BLOBs Oui Oui, en Uint8Array
Support ORM TypeORM intégré capacitor driver Drivers personnalisés (Kysely fonctionne bien)
Configuration native supplémentaire Web : copier sql-wasm.wasm iOS NSAllowsLocalNetworking, règle cleartext localhost Android
Mainteneur communauté Capacitor Capgo

Quand rester sur le plugin communautaire

Soyez pragmatiques. Restez si :

  • vous déployez une Electron build,
  • vous utilisez TypeORM (son pilote capacitor est écrit pour le plugin communautaire, voir TypeORM avec Capacitor),
  • you rely on Import/export JSON ou synchronisation des tables et ne souhaitez pas les reconstruire,
  • vos requêtes sont légères et l’application fonctionne bien aujourd’hui.

Quand Fast SQL est le meilleur choix

Migrez si :

  • vous chargez ou écrivez de grands jeux de données (moteurs de synchronisation, catalogues hors ligne, index de recherche, logs CRDT),
  • vous souhaitez chiffrer uniquement certaines bases de données, avec des clés issues du Keychain ou du Keystore,
  • vous ne voulez pas inclure SQLCipher dans une application qui n’en a pas besoin,
  • vous voulez un vrai fichier SQLite sur le web via OPFS plutôt qu'une copie en mémoire,
  • vous voulez un simple API : connexion, requête, exécution, transaction.

Pour un guide complet de Fast SQL, voir Comment utiliser SQLite dans une application Capacitor.

Étape 1 : installer Fast SQL à côté de l’ancien plugin

Gardez le plugin communautaire installé pour une release afin de pouvoir migrer les données existantes.

bun add @capgo/capacitor-fast-sql
bunx cap sync

Configuration native requise par Fast SQL :

  • iOS: NSAppTransportSecurity > NSAllowsLocalNetworking = true dans Info.plist.
  • Android: un network_security_config.xml qui autorise le trafic en clair vers localhost et 127.0.0.1, référencé dans le manifest.
  • Chiffrement: net.zetetic:sqlcipher-android dans le module d'application Android et le CapgoCapacitorFastSql/SQLCipher pod sur iOS. Détails dans chiffrer SQLite dans Capacitor.
  • Le Web: Cross-Origin-Opener-Policy: same-origin et Cross-Origin-Embedder-Policy: require-corp en-têtes pour OPFS.

Étape 2 : mapper l’API

Ouvrir une base de données

Avant :

import { CapacitorSQLite, SQLiteConnection } from '@capacitor-community/sqlite';

const sqlite = new SQLiteConnection(CapacitorSQLite);
const db = await sqlite.createConnection('app', false, 'no-encryption', 1, false);
await db.open();

Après :

import { FastSQL } from '@capgo/capacitor-fast-sql';

const db = await FastSQL.connect({ database: 'app', walMode: true, performancePresets: true });

FastSQL.connect() renvoie la connexion existante si la base de données est déjà ouverte, il n’y a donc isConnection / retrieveConnection manipulation.

Correspondance des méthodes

Plugin communautaire SQL rapide
createConnection() + open() FastSQL.connect({ database })
closeConnection(name) FastSQL.disconnect(name)
db.query(sql, values) renvoie { values } db.query(sql, params) renvoie les lignes
db.run(sql, values) renvoie { changes: { changes, lastId } } db.run(sql, params) renvoie { rowsAffected, insertId }
db.execute(multiStatementString) Un db.execute(sql) par instruction, ou db.executeBatch([...])
db.executeSet(set) db.executeBatch(ops) par { statement, params }
beginTransaction / commitTransaction / rollbackTransaction db.transaction(async (tx) => ...) ou beginTransaction() / commit() / rollback()
addUpgradeStatement / upgradeStatements Un PRAGMA user_version migration runner
copyFromAssets() Copier un bundle .db en Directory.Data avant la première connect()
setEncryptionSecret() + mode: 'secret' encrypted: true, encryptionKey sur connect()
initWebStore() / saveToStore() Inutile, OPFS persiste les écritures

Requêtes et écritures

Avant :

const res = await db.query('SELECT * FROM notes WHERE pinned = ?', [1]);
const notes = res.values ?? [];

const { changes } = await db.run('INSERT INTO notes (title) VALUES (?)', ['Hello']);
const id = changes?.lastId;

Après :

const notes = await db.query('SELECT * FROM notes WHERE pinned = ?', [1]);

const { insertId } = await db.run('INSERT INTO notes (title) VALUES (?)', ['Hello']);

Les deux plugins renvoient les lignes sous forme d'objets indexés par nom de colonne.

Exécution de plusieurs instructions

Le plugin communautaire execute() accepte une chaîne de caractères contenant plusieurs instructions séparées par ;. Fast SQL prépare une instruction par appel. Découpez votre SQL, ou mieux, conservez les instructions sous forme de tableau :

const schema = [
  'CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL)',
  'CREATE INDEX IF NOT EXISTS idx_notes_title ON notes (title)',
];

await db.executeBatch(schema.map((statement) => ({ statement })));

Ne découpez pas sur ; avec une approche naïve split(';') naïf si une instruction contient un corps de trigger ou une chaîne littérale avec un point-virgule.

Mettre à jour les instructions vers user_version

Le plugin communautaire stocke la version du schéma dans PRAGMA user_version également. Si vous conservez les mêmes numéros de version, les bases de données de vos utilisateurs existants sont déjà à la bonne version après la migration de fichiers de l'étape 3.

import type { SQLConnection } from '@capgo/capacitor-fast-sql';

// Same content as your old upgrade statements, index 0 = version 1
const versions: string[][] = [
  ['CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL)'],
  ['ALTER TABLE notes ADD COLUMN pinned INTEGER NOT NULL DEFAULT 0'],
];

export async function migrate(db: SQLConnection) {
  const [{ user_version }] = await db.query('PRAGMA user_version');
  for (let v = Number(user_version); v < versions.length; v++) {
    await db.transaction(async (tx) => {
      for (const statement of versions[v]) await tx.execute(statement);
      await tx.execute(`PRAGMA user_version = ${v + 1}`);
    });
  }
}

Vérifiez le chemin réel user_version sur une installation existante avant de vous y fier. Si vous n’êtes pas sûr, loggez-le depuis l’ancien plugin dans votre release actuelle.

Bases de données préremplies

Le plugin communautaire copyFromAssets() copie les bases de données bundled au premier lancement. Avec Fast SQL, placez le fichier là où le plugin le cherche avant de vous connecter. Fast SQL stocke <name>.db dans Documents sur iOS et dans le répertoire files de l’app sur Android, qui sont tous deux Directory.Data dans @capacitor/filesystem:

import { Directory, Filesystem } from '@capacitor/filesystem';
import { FastSQL } from '@capgo/capacitor-fast-sql';

async function openCatalog() {
  const exists = await Filesystem.stat({ path: 'catalog.db', directory: Directory.Data })
    .then(() => true)
    .catch(() => false);

  if (!exists) {
    const blob = await (await fetch('/assets/catalog.db')).blob();
    const base64 = await new Promise<string>((resolve) => {
      const reader = new FileReader();
      reader.onloadend = () => resolve((reader.result as string).split(',')[1]);
      reader.readAsDataURL(blob);
    });
    await Filesystem.writeFile({ path: 'catalog.db', data: base64, directory: Directory.Data });
  }
  return FastSQL.connect({ database: 'catalog', readOnly: true });
}

Pour les gros fichiers, téléchargez-les par morceaux ou livrez un fichier compressé et utilisez la copie native du plugin file plutôt que du base64 en JavaScript.

Step 3: migrate existing user data

Les utilisateurs qui mettent à jour votre app ont une base de données créée par le plugin communautaire. Elle se trouve à un autre chemin, avec un autre nom :

Plugin communautaire SQL rapide
iOS Documents/<name>SQLite.db (par défaut iosDatabaseLocation) Documents/<name>.db
Android databases/<name>SQLite.db files/<name>.db

Option A : copier le fichier (bases de données non chiffrées)

La plus rapide, et elle conserve tout : index, triggers, user_version.

import { CapacitorSQLite, SQLiteConnection } from '@capacitor-community/sqlite';
import { Directory, Filesystem } from '@capacitor/filesystem';
import { Preferences } from '@capacitor/preferences';

export async function moveCommunityDatabase(name: string): Promise<void> {
  const done = await Preferences.get({ key: `sqlite-moved:${name}` });
  if (done.value === '1') return;

  const sqlite = new SQLiteConnection(CapacitorSQLite);
  const exists = (await sqlite.isDatabase(name)).result;
  if (exists) {
    const conn = await sqlite.createConnection(name, false, 'no-encryption', 1, false);
    await conn.open();
    await conn.execute('PRAGMA wal_checkpoint(TRUNCATE)', false).catch(() => undefined);
    const { url } = await conn.getUrl();
    await sqlite.closeConnection(name, false);

    if (url) {
      await Filesystem.copy({ from: url, to: `${name}.db`, toDirectory: Directory.Data });
    }
  }
  await Preferences.set({ key: `sqlite-moved:${name}`, value: '1' });
}

Appel à faire avant le premier FastSQL.connect({ database: name }). Remarques :

  • getUrl() (iOS et Android) renvoie l’URL du fichier de base de données. Passer un file:// URL sans directory permet au plugin Filesystem de lire n’importe quel chemin dans votre sandbox.
  • Fermer la connexion et effectuer un checkpoint en premier garantit qu'aucune écriture récente ne reste dans un -wal fichier.
  • Conservez l'ancien fichier pendant une release, puis supprimez-le plus tard avec la delete() méthode une fois que vous êtes sûr de vous. Supprimez ensuite le plugin communautaire.

Option B : copier les lignes en JavaScript (bases de données chiffrées ou changement de schéma)

Si la source est chiffrée avec le secret stocké par le plugin communautaire, la voie la plus simple et la plus sûre est de laisser ce plugin déchiffrer et copier les lignes dans une nouvelle base de données Fast SQL. Cela vous permet également de chiffrer avec une nouvelle clé par base de données issue du stockage sécurisé.

import { CapacitorSQLite, SQLiteConnection } from '@capacitor-community/sqlite';
import { FastSQL } from '@capgo/capacitor-fast-sql';
import { Preferences } from '@capacitor/preferences';

export async function copyRows(name: string, target: { encryptionKey?: string }) {
  // Record which destination mode was migrated, so a later call with a different target fails loudly
  const mode = target.encryptionKey ? 'encrypted' : 'plain';
  const done = await Preferences.get({ key: `sqlite-copied:${name}` });
  if (done.value === mode) return;
  if (done.value) throw new Error(`${name} was already copied as ${done.value}, not ${mode}`);

  const sqlite = new SQLiteConnection(CapacitorSQLite);
  const src = await sqlite.createConnection(name, true, 'secret', 1, false);
  await src.open();

  try {
    const dst = await FastSQL.connect({
      database: name,
      encrypted: Boolean(target.encryptionKey),
      encryptionKey: target.encryptionKey,
    });

    const objects = ((await src.query(
      `SELECT type, name, sql FROM sqlite_master
       WHERE sql IS NOT NULL AND name NOT LIKE 'sqlite_%'`,
    )).values ?? []) as { type: string; name: string; sql: string }[];
    const tables = objects.filter((o) => o.type === 'table');
    const others = objects.filter((o) => o.type !== 'table');
    const version = (await src.query('PRAGMA user_version')).values?.[0]?.user_version ?? 0;

    await dst.transaction(async (tx) => {
      for (const t of tables) await tx.execute(t.sql);
      for (const t of tables) {
        const rows = (await src.query(`SELECT * FROM "${t.name}"`)).values ?? [];
        if (!rows.length) continue;
        const cols = Object.keys(rows[0]);
        const statement = `INSERT INTO "${t.name}" (${cols.map((c) => `"${c}"`).join(', ')}) VALUES (${cols.map(() => '?').join(', ')})`;
        await tx.executeBatch(rows.map((r) => ({ statement, params: cols.map((c) => r[c]) })));
      }
      for (const o of others) await tx.execute(o.sql);
      await tx.execute(`PRAGMA user_version = ${Number(version)}`);
    });

    // Only mark the copy as done once the transaction has committed
    await Preferences.set({ key: `sqlite-copied:${name}`, value: mode });
  } finally {
    await sqlite.closeConnection(name, false);
  }
}

Utiliser 'no-encryption' et false dans createConnection si la source n'est pas chiffrée. Le plugin communautaire nécessite iosIsEncryption / androidIsEncryption dans capacitor.config pour ses modes chiffrés, que vous avez déjà si vous les avez utilisés. Pour les grandes tables, parcourez par pages avec WHERE rowid > ? ORDER BY rowid LIMIT 5000.

Les deux plugins utilisent les valeurs par défaut de SQLCipher 4, donc un fichier chiffré peut aussi s’ouvrir directement dans Fast SQL avec le même mot de passe. Le plugin communautaire gère ce mot de passe en interne, mais la copie des lignes évite de dépendre de ce détail.

Étape 4 : supprimer l'ancien plugin

Après une ou deux releases avec la migration en place :

bun remove @capacitor-community/sqlite jeep-sqlite
bunx cap sync

Supprimer sql-wasm.wasm de vos assets et de tout jeep-sqlite configuration en main.ts. Retirer le plugin retire aussi sa dépendance SQLCipher, donc si vous n’activez pas le chiffrement de Fast SQL, les binaires iOS et Android sont plus petits.

La migration de données code est en JavaScript, elle peut donc être livrée avec les live updates Capgo, mais la release qui ajoute Fast SQL est un changement natif et nécessite un build pour les stores. Déployez d’abord le build natif. Build Capgo peut le générer dans le cloud si vous n’avez pas de Mac sous la main (les uploads vers l’App Store exigent Xcode 26 ou plus depuis avril 2026).

Troubleshooting

Fast SQL se connecte, mais toutes les requêtes échouent sur iOS. NSAllowsLocalNetworking est absent de l’application Info.plist, ou l’application n’a pas été recompilée après son ajout.

Android : CLEARTEXT communication to localhost not permitted. Ajoutez localhost domain-config à votre configuration de sécurité réseau. Si vous en aviez déjà une pour le SSL pinning, fusionnez-les.

Seule une partie de mon schéma a été créée. Vous avez passé une chaîne multi-instructions à execute(). Découpez-l’en un tableau.

INSERT ... RETURNING ne renvoie rien sur Android. Dans Fast SQL 8.2, Android ne renvoie des lignes que pour les instructions commençant par SELECT, PRAGMA ou EXPLAIN. Utilisez insertId depuis run().

Booleans do not match on Android reads. Passer 1/0 au lieu de true/false dans les paramètres de requête. Android lie les paramètres de requête en lecture au type texte.

La base de données copiée est vide. Le plugin communautaire a été configuré avec un iosDatabaseLocation, ou le nom de la source diffère. Journalisez l’URL renvoyée par getUrl().

Les données web de l’ancien plugin ont disparu. Le plugin communautaire stockait les données web dans IndexedDB via jeep-sqlite, Fast SQL utilise OPFS. Resynchronisez les clients web depuis votre backend, ou exportez avec l’ancien plugin exportToJson et importez les lignes dans Fast SQL.

Autres options

  • @capgo/capacitor-data-storage-sqlite si ce dont vous avez vraiment besoin est un store clé-valeur basé sur SQLite. Voir le stockage clé-valeur dans Capacitor.
  • Un ORM ou un query builder par-dessus: Kysely fonctionne avec Fast SQL via un court driver personnalisé. Drizzle convient mieux au plugin communautaire pour l’instant.

L’API Fast SQL complet se trouve dans le doc du plugin et sur le page du plugin.

Live updates pour les apps Capacitor

Lorsqu'un bug de la couche web est en ligne, déployez le correctif via Capgo au lieu d'attendre des jours l'approbation des stores. Les utilisateurs reçoivent la mise à jour en arrière-plan, tandis que les changements natifs suivent le circuit de validation habituel.

Les derniers articles du blog

Capgo vous offre les meilleures informations pour créer une application mobile vraiment professionnelle.