Salta al contenuto principale

Alternative a @capacitor-community/sqlite nel 2026

Cerchi un'alternativa a @capacitor-community/sqlite? Confrontala con Capgo Fast SQL e migra passo dopo passo i tuoi Capacitor SQLite code e i dati degli utenti.

Logo Capgo per una guida sulle alternative al plugin SQLite community di Capacitor

L'alternativa principale a @capacitor-community/sqlite su Capacitor 8 è @capgo/capacitor-fast-sql: un plugin SQLite nativo per iOS, Android e web che mantiene SQLCipher opzionale e invia i dati delle query tramite un canale HTTP locale invece del bridge Capacitor. Questa guida confronta onestamente i due plugin, spiega quando restare sul plugin community e illustra come migrare sia il tuo code sia i database esistenti dei tuoi utenti.

Perché i team cercano un'alternativa

@capacitor-community/sqlite è stato il plugin SQLite predefinito per Capacitor per anni ed è ancora mantenuto (la versione 8.1.1 è uscita ad agosto 2026). Le ragioni per cui le persone cambiano sono specifiche:

  1. Serializzazione del bridge. Ogni risultato passa attraverso il bridge Capacitor come JSON. Va bene per query piccole, ma diventa evidente quando si caricano migliaia di righe per sync o ricerca.
  2. SQLCipher è sempre incluso. Il plugin dipende da SQLCipher su iOS e Android anche se non crittografhi mai. Questo aumenta la dimensione nativa e significa che il tuo binario contiene code di crittografia che devi considerare nelle risposte di export compliance.
  3. Modello di storage web. Sul web utilizza il jeep-sqlite componente: sql.js in memoria, persistito in IndexedDB quando chiami saveToStore(). Devi ricordarti di salvare, e l'intero database risiede in memoria.
  4. Un'ampia superficie API. Wrapper di connessione, controlli di coerenza, connessioni in sola lettura o non conformi, tabelle sincronizzate, import/export JSON. Potente, ma complesso per un semplice “run SQL”.
  5. Una passphrase per app. La crittografia usa un unico segreto memorizzato dal plugin (setEncryptionSecret), condiviso da ogni database crittografato nell'app.

Confronto affiancato

@capacitor-community/sqlite 8.1 @capgo/capacitor-fast-sql 8.2
Piattaforme iOS, Android, Web, Electron iOS, Android, Web
Percorso dati bridge Capacitor Server HTTP locale con token per connessione (nativo)
Motore web jeep-sqlite (sql.js + IndexedDB, manuale saveToStore) Ufficiale @sqlite.org/sqlite-wasm con OPFS, le scritture vanno sul file
Crittografia SQLCipher sempre incluso, una passphrase gestita dal plugin SQLCipher opzionale, chiave passata per database su connect()
Swift Package Manager Sì (Package.swift) Sì (Package.swift)
Versionamento dello schema upgradeStatements / addUpgradeStatement Il tuo, di solito PRAGMA user_version
Import/export JSON, sincronizzazione tabelle Sì No
Database prepopolato dagli asset copyFromAssets() Copia prima il file manualmente connect()
Helper key-value No KeyValueStore
BLOB Sì Sì, come Uint8Array
Supporto ORM TypeORM integrato capacitor driver Driver personalizzati (Kysely funziona bene)
Setup nativo aggiuntivo Web: copia sql-wasm.wasm iOS NSAllowsLocalNetworking, regola cleartext localhost per Android
Manutentore community Capacitor Capgo

Quando restare sul plugin della community

Sii pratico. Resta se:

  • pubblici un Electron build,
  • usavi TypeORM (il suo driver capacitor è scritto per il plugin della community, vedi TypeORM con Capacitor),
  • dipendi da import/export JSON o tabelle di sincronizzazione e non volete rifarli da zero,
  • le vostre query sono piccole e l'app funziona bene oggi.

Quando Fast SQL è la scelta migliore

Migrate se:

  • caricate o scrivete dataset di grandi dimensioni (motori di sync, cataloghi offline, indici di ricerca, log CRDT),
  • vuoi la crittografia solo per alcuni database, con chiavi da Keychain o Keystore,
  • non vuoi SQLCipher in un'app che non ne ha bisogno,
  • vuoi un vero file SQLite sul web tramite OPFS invece di una copia in memoria,
  • vuoi un API semplice: connetti, interroga, esegui, transazione.

Per una guida completa su Fast SQL, consulta Come usare SQLite in un'app Capacitor.

Passaggio 1: installa Fast SQL accanto al vecchio plugin

Mantieni installato il plugin della community per un rilascio, così potrai migrare i dati esistenti.

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

Configurazione nativa necessaria per Fast SQL:

  • iOS: NSAppTransportSecurity > NSAllowsLocalNetworking = true nella Info.plist.
  • Android: un network_security_config.xml che consente il cleartext verso localhost e 127.0.0.1, referenziato dal manifest.
  • Crittografia: net.zetetic:sqlcipher-android nel modulo dell'app Android e nel CapgoCapacitorFastSql/SQLCipher pod su iOS. Dettagli in criptare SQLite in Capacitor.
  • Web: Cross-Origin-Opener-Policy: same-origin e Cross-Origin-Embedder-Policy: require-corp header per OPFS.

Passo 2: mappare il API

Apertura di un database

Prima:

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();

Dopo:

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

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

FastSQL.connect() restituisce la connessione esistente se il database è già aperto, quindi non c'è isConnection / retrieveConnection dance.

Mappatura dei metodi

Plugin della community SQL veloce
createConnection() + open() FastSQL.connect({ database })
closeConnection(name) FastSQL.disconnect(name)
db.query(sql, values) restituisce { values } db.query(sql, params) restituisce righe
db.run(sql, values) restituisce { changes: { changes, lastId } } db.run(sql, params) restituisce { rowsAffected, insertId }
db.execute(multiStatementString) Uno db.execute(sql) per statement, oppure db.executeBatch([...])
db.executeSet(set) db.executeBatch(ops) con { statement, params }
beginTransaction / commitTransaction / rollbackTransaction db.transaction(async (tx) => ...) oppure beginTransaction() / commit() / rollback()
addUpgradeStatement / upgradeStatements Un PRAGMA user_version migration runner
copyFromAssets() Copia un bundle .db all'interno Directory.Data prima del primo connect()
setEncryptionSecret() + mode: 'secret' encrypted: true, encryptionKey su connect()
initWebStore() / saveToStore() Non necessario, OPFS persiste le scritture

Query e scritture

Prima:

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;

Dopo:

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

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

Both plugins return rows as objects keyed by column name.

Esecuzione multi-statement

Il plugin della community execute() accetta una stringa con più statement separati da ;. Fast SQL prepara un solo statement per chiamata. Dividi il tuo SQL, o meglio, mantieni gli statement in un array:

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 })));

Non fare split su ; con un approccio ingenuo split(';') se uno statement contiene un trigger body o una string literal con un punto e virgola.

Aggiorna gli statement a user_version

Il plugin della community salva la versione dello schema in PRAGMA user_version anch'esso. Se mantieni gli stessi numeri di versione, i database dei tuoi utenti esistenti sono già alla versione corretta dopo la migrazione del file nel passaggio 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}`);
    });
  }
}

Verifica quella effettiva user_version su un'installazione esistente prima di affidarti a questo. Se non sei sicuro, loggala dal vecchio plugin nella tua release attuale.

Database precompilati

Il plugin della community copyFromAssets() copia i database inclusi al primo avvio. Con Fast SQL, posiziona il file dove il plugin lo cerca prima di connetterti. Fast SQL salva <name>.db nella Documents su iOS e nella directory files dell'app su Android, che sono entrambe Directory.Data nella @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 });
}

Per i file di grandi dimensioni, scaricali a blocchi oppure includi un file compresso e usa la copia nativa del plugin file invece del base64 in JavaScript.

Passo 3: migrazione dei dati utente esistenti

Gli utenti che aggiornano la tua app hanno un database creato dal plugin community. Si trova in un percorso diverso e con un nome diverso:

Plugin community SQL veloce
iOS Documents/<name>SQLite.db (default iosDatabaseLocation) Documents/<name>.db
Android databases/<name>SQLite.db files/<name>.db

Opzione A: copia del file (database non crittografati)

La più veloce, e conserva tutto: indici, trigger, 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' });
}

Chiamalo prima del primo FastSQL.connect({ database: name }). Note:

  • getUrl() (iOS e Android) restituisce l'URL del file del database. Passare un file:// URL completo senza directory permette al plugin Filesystem di leggere da qualsiasi percorso all'interno del tuo sandbox.
  • Chiudere la connessione e fare il checkpoint prima assicura che non restino scritture recenti in un -wal file.
  • Mantieni il vecchio file per un rilascio e eliminalo in seguito con il delete() metodo una volta che sei sicuro. Poi rimuovi il plugin della community.

Opzione B: copia delle righe in JavaScript (database crittografati, o modifica dello schema)

Quando la sorgente è cifrata con il segreto memorizzato del plugin community, il percorso più semplice e sicuro è lasciare che quel plugin decifri e copi le righe in un nuovo database Fast SQL. Questo consente anche di cifrare con una nuova chiave per database da storage sicuro.

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);
  }
}

Usa 'no-encryption' e false nella createConnection se la sorgente non è cifrata. Il plugin community richiede iosIsEncryption / androidIsEncryption nella capacitor.config per le sue modalità crittografate, che hai già se le hai usate. Per tabelle grandi, usa la paginazione con WHERE rowid > ? ORDER BY rowid LIMIT 5000.

Entrambi i plugin usano i default di SQLCipher 4, quindi un file criptato può essere aperto direttamente in Fast SQL con la stessa passphrase. Il plugin della community, però, gestisce quella passphrase internamente, e la copia delle righe evita di dipendere da questo dettaglio.

Passaggio 4: rimuovi il vecchio plugin

Dopo uno o due rilasci con la migrazione in atto:

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

Rimuovi sql-wasm.wasm dai tuoi asset e qualsiasi jeep-sqlite setup in main.ts. Rimuovere il plugin elimina anche la dipendenza da SQLCipher, quindi se non scegli la crittografia di Fast SQL, i binari iOS e Android diventano più leggeri.

La migrazione dei dati code è in JavaScript, quindi può essere distribuita con Live updates di Capgo, ma il rilascio che aggiunge Fast SQL è una modifica nativa e richiede una build per lo store. Distribuisci prima la build nativa. Build Capgo può produrlo nel cloud se non hai un Mac a disposizione (gli upload su App Store richiedono Xcode 26 o superiore da aprile 2026).

Risoluzione problemi

Fast SQL si connette, ma ogni query fallisce su iOS. NSAllowsLocalNetworking is missing from the app’s Info.plist, oppure l'app non è stata ricostruita dopo averla aggiunta.

Android: CLEARTEXT communication to localhost not permitted. Aggiungi il localhost domain-config alla tua network security config. Se ne avevi già una per l'SSL pinning, uniscile.

È stata creata solo una parte del mio schema. Hai passato una stringa con più statement a execute(). Dividila in un array.

INSERT ... RETURNING non restituisce nulla su Android. In Fast SQL 8.2, Android restituisce righe solo per le istruzioni che iniziano con SELECT, PRAGMA oppure EXPLAIN. Usa insertId da run().

I booleani non corrispondono nelle letture su Android. Passa 1/0 invece di true/false nei parametri di query. Android associa i parametri read-query come testo.

Il database copiato è vuoto. Il plugin community era configurato con un iosDatabaseLocation, oppure il nome della sorgente è diverso. Registra nel log l'URL restituito da getUrl().

I dati web del vecchio plugin sono andati persi. Il plugin community memorizzava i dati web in IndexedDB tramite jeep-sqlite, Fast SQL usa OPFS. Risincronizza i client web dal tuo backend, oppure esporta con il exportToJson vecchio plugin e importa le righe in Fast SQL.

Altre opzioni

  • @capgo/capacitor-data-storage-sqlite se ciò di cui hai davvero bisogno è un key-value store a stringhe basato su SQLite. Vedi lo storage key-value in Capacitor.
  • Un ORM o query builder sopra: Kysely funziona con Fast SQL tramite un breve driver personalizzato. Drizzle attualmente si adatta meglio al plugin della community.

Il API completo di Fast SQL è nel docs del plugin e sul pagina del plugin.

Live updates per le app Capacitor

Quando un bug nel web-layer è live, rilascia la fix tramite Capgo invece di aspettare giorni l'approvazione degli store. Gli utenti ricevono l'update in background, mentre le modifiche native restano nel normale processo di review.

Ultimi articoli dal blog

Capgo ti offre gli insight migliori che ti servono per creare un'app mobile davvero professionale.