La principale alternativa a @capacitor-community/sqlite su Capacitor 8 è @capgo/capacitor-fast-sql: un plugin SQLite nativo per iOS, Android e il web che mantiene SQLCipher facoltativo e invia i dati delle query attraverso un canale HTTP locale anziché il ponte Capacitor. Questa guida confronta le due onestamente, dice quando restare sulla plugin della community e guida attraverso la migrazione sia dei tuoi code che dei database degli utenti esistenti.
Why le squadre cercano un'alternativa
@capacitor-community/sqlite è stato il plugin SQLite predefinito per Capacitor per anni e viene ancora mantenuto (8.1.1 rilasciato nel agosto 2026). Le ragioni per cui le persone si spostano sono specifiche:
- Serializzazione del Bridge. Tutti i risultati passano attraverso il bridge di Capacitor come JSON. Ciò è accettabile per piccole query, e notevole quando si caricano migliaia di righe per la sincronizzazione o la ricerca.
- SQLCipher è sempre incluso. Il plugin dipende da SQLCipher su iOS e Android anche se non si crittografano mai. Ciò aggiunge dimensioni native e significa che il tuo binario contiene crittografia code che devi tenere conto nelle risposte di compliance all'esportazione.
- Modello di archiviazione web. Sul web utilizza il componente: sql.js in memoria, persistito in IndexedDB quando si chiama
jeep-sqliteDevi ricordarti di salvare, e l'intera database vive in memoria.saveToStore()Ricordate di salvare e il database intero vive nella memoria. - Una grande superficie API. Raccoglitori di connessioni, controlli di consistenza, connessioni di sola lettura e non conformi, tabelle sincrone, importazione ed esportazione JSON. Potente, ma molto da imparare per "eseguire SQL".
- Una frase segreta per app. L'encryption utilizza un segreto singolo memorizzato dal plugin (
setEncryptionSecret), condiviso da ogni database crittografato nell'app.
Confronto a faccia a faccia
@capacitor-community/sqlite 8.1 |
@capgo/capacitor-fast-sql 8.2 |
|
|---|---|---|
| Piattaforme | iOS, Android, Web, Electron | iOS, Android, Web |
| Percorso dei dati | ponte Capacitor | Server HTTP locale con un token per connessione (nativo) |
| Motore web | jeep-sqlite (sql.js + IndexedDB, manuale saveToStore) |
Ufficiale @sqlite.org/sqlite-wasm con OPFS, le scritture vanno nel file |
| Criptazione | SQLCipher sempre inclusa, una passphrase gestita da plugin | SQLCipher facoltativo, chiave passata per database su connect() |
| Manager dei Pacchetti Swift | Sì (Package.swift) |
Sì (Package.swift) |
| Versione dello schema | upgradeStatements / addUpgradeStatement |
La tua, di solito PRAGMA user_version |
| Importazione/Esportazione JSON, sincronizzazione delle tabelle | Sì | No |
| Database prepopolato da asset | copyFromAssets() |
Copia il file prima di connect() |
| Assistente chiave-valore | No | KeyValueStore |
| BLOB | Sì | Sì, come Uint8Array |
| Sostegno ORM | Driver integrato TypeORM capacitor driver |
Driveri personalizzati (Kysely funziona bene) |
| Configurazione nativa aggiuntiva | Copia: web sql-wasm.wasm |
iOS NSAllowsLocalNetworking, regola localhost cleartext per Android |
| Responsabile | comunità di Capacitor | Capgo |
Quando mantenere il plugin della community
hai distribuito un
- Electron costruisci costruisci,
- Utilizzate TypeORM (il suo capacitor driver è scritto contro il plugin della community, vedi TypeORM con Capacitor),
- Relyete import/export o sincronizzazione di tabelle e non volete ricostruirle,
- Le vostre query sono piccole e l'app funziona bene oggi.
Quando Fast SQL è la scelta migliore
Muovete se:
- Caricate o scrivete grandi set di dati (motori di sincronizzazione, cataloghi offline, indici di ricerca, registri CRDT),
- Volete l'encryption solo per alcune database, con chiavi dal Keychain o Keystore,
- non desiderate SQLCipher in un'applicazione che non ne ha bisogno
- desiderate un file SQLite reale sul web attraverso OPFS al posto di una copia in memoria
- desiderate un semplice API: connettersi, interrogare, eseguire, transazione.
Per una guida completa di Fast SQL stesso, vedere Come utilizzare SQLite in un'app Capacitor.
Passo 1: installare Fast SQL accanto al vecchio plugin
Conservate installato il plugin della community per una versione per poter migrare i dati esistenti.
bun add @capgo/capacitor-fast-sql
bunx cap sync
Configurazione nativa che Fast SQL richiede:
- iOS:
NSAppTransportSecurity>NSAllowsLocalNetworking=trueinInfo.plist. - Android: a
network_security_config.xmlche consente il testo chiarolocalhoste127.0.0.1, riferito dal manifesto. - Criptazione:
net.zetetic:sqlcipher-androidnel modulo dell'app Android e nelCapgoCapacitorFastSql/SQLCipherpod su iOS. Dettagli in criptare SQLite in Capacitor. - Web:
Cross-Origin-Opener-Policy: same-origineCross-Origin-Embedder-Policy: require-corpintestazioni per OPFS.
Passo 2: mappa il API
Aprire una banca dati
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 la banca dati è già aperta, quindi non ci sono isConnection / retrieveConnection dance.
Mappatura del metodo
| Plugin della comunità | 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) |
One db.execute(sql) per statement, o db.executeBatch([...]) |
db.executeSet(set) |
db.executeBatch(ops) con { statement, params } |
beginTransaction / commitTransaction / rollbackTransaction |
db.transaction(async (tx) => ...) o beginTransaction() / commit() / rollback() |
addUpgradeStatement / upgradeStatements |
Una PRAGMA user_version un migratore di migrazione |
copyFromAssets() |
Copia un pacchetto .db into Directory.Data prima della prima connect() |
setEncryptionSecret() + mode: 'secret' |
encrypted: true, encryptionKey on connect() |
initWebStore() / saveToStore() |
Non necessario, OPFS persiste le scritture |
Copy a bundled
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']);
Entrambi i plugin restituiscono le righe come oggetti chiave nome colonna.
Esecuzione multi-statement
La comunità execute() accetta una stringa con più istruzioni separate da ;Fast SQL prepara una istruzione per chiamata. Spegna la tua SQL, o meglio, conserva le istruzioni come 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 dividere su ; con un approccio ingenuo split(';') se qualsiasi istruzione contiene un corpo di trigger o una stringa letterale con un punto e virgola.
Aggiorna le dichiarazioni di upgrade a user_version
Il plugin della comunità memorizza la versione dello schema in PRAGMA user_version Se mantenete gli stessi numeri di versione, i database dei vostri utenti esistenti sono già alla versione giusta dopo la migrazione del file nel passo 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}`);
});
}
}
Controlla l'effettivo user_version Sul tuo installazione esistente prima di affidarti a questo. Registra il messaggio dall'antico plugin nella tua versione corrente se non sei sicuro.
Databases prepopolate
La plugin della comunità copyFromAssets() Copia le database bundle alla prima esecuzione. Con Fast SQL, posiziona il file dove il plugin lo cerca prima di connettersi. Fast SQL memorizza <name>.db in Documents in iOS e nell'applicazione files directory su Android, che sono entrambi Directory.Data in @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 grandi, scaricali in chunk o invia un file compresso e utilizza il plugin di file nativo per copiare al posto di base64 in JavaScript.
Passo 3: migra i dati degli utenti esistenti
Utenti che aggiornano l'applicazione hanno una banca dati creata dal plugin della community. Vive in un percorso diverso con un nome diverso:
| Plugin della community | SQL veloce | |
|---|---|---|
| iOS | Documents/<name>SQLite.db (default iosDatabaseLocation) |
Documents/<name>.db |
| Android | databases/<name>SQLite.db |
files/<name>.db |
Opzione A: copia il file (banche dati non crittografate)
Più veloce e mantiene 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 della prima FastSQL.connect({ database: name }). Note:
getUrl()Ritorna l'URL del file di database (iOS e Android). Passa un file di database completofile://__CAPGO_KEEP_0__directoryconsente al plugin Filesystem di leggere da qualsiasi percorso all'interno del tuo sandbox.- Chiudendo la connessione e effettuando il checkpoint prima si assicura che nessuna scrittura recente sia lasciata in un
-walfile. - Mantieni il file vecchio per una versione e cancellalo in seguito con la connessione della community
delete()Opzione B: copia righe in JavaScript (database criptati, o un cambiamento di schema)
Opzione B: copia righe in JavaScript (database crittografate, o un cambiamento di schema)
When the source is encrypted with the community plugin’s stored secret, the simplest safe path is to let that plugin decrypt and copy rows into a new Fast SQL database. This also lets you encrypt with a new per-database key from secure storage.
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);
}
}
e 'no-encryption' in false in createConnection se la fonte non è crittografata. Il plugin della community richiede iosIsEncryption / androidIsEncryption in capacitor.config per le sue modalità crittografate, che già avete se le avete utilizzate. Per le grandi tabelle, passate pagina con WHERE rowid > ? ORDER BY rowid LIMIT 5000.
Entrambi i plugin utilizzano SQLCipher 4 di default, quindi un file crittografato può anche essere aperto direttamente in Fast SQL con la stessa passphrase. Il plugin della community gestisce internamente quella passphrase, e la copia di riga evita di dipendere da quel dettaglio.
Passo 4: rimuovi l'antico 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 da qualsiasi jeep-sqlite impostazione di main.ts. La rimozione del plugin elimina anche la sua dipendenza da SQLCipher, quindi se non opti per l'encryption di Fast SQL, i binari iOS e Android diventano più piccoli.
La migrazione dei dati code è JavaScript, quindi può essere spedita con Capgo aggiornamenti livema la release che aggiunge Fast SQL è un cambiamento nativo e richiede una build del store. Invia la build nativa per prima. Capgo Build puoi produrlo in cloud se non hai un Mac a portata di mano (le pubblicazioni su App Store richiedono Xcode 26 o successivi dal 2026).
Troubleshooting
Fast SQL si connette ma ogni query fallisce su iOS. NSAllowsLocalNetworking è assente nell'applicazione Info.plisto l'app non è stata ricostruita dopo averlo aggiunto.
Android: CLEARTEXT communication to localhost not permitted. Aggiungi il localhost domain-config Aggiungi la configurazione di sicurezza della rete al tuo setup. Se già avevi una configurazione per la pinning SSL, uniscile.
Hai passato una stringa multi-statement a Hai passato una stringa multi-statement a execute()non restituisce nulla su Android.
INSERT ... RETURNING Risoluzione dei problemi In Fast SQL 8.2, Android restituisce righe solo per le dichiarazioni che iniziano con SELECT, PRAGMA o EXPLAIN. Utilizza insertId da run().
Booleani non corrispondono su letture Android. Passa 1/0 al posto di true/false Parametri di query. Android lega i parametri di query di lettura come testo.
La copia del database è vuota. Il plugin della community è stato configurato con un custom iosDatabaseLocation, o il nome della fonte differisce. Registra l'URL restituito da getUrl().
Dati web dall'antica plugin sono andati perduti. The community plugin memorizzava i dati web in IndexedDB attraverso jeep-sqlite, Fast SQL utilizza OPFS. Ri-sincronizza i client web dal tuo backend, o esporta con il vecchio plugin's exportToJson e importa righe in Fast SQL.
Altre opzioni
@capgo/capacitor-data-storage-sqlitese ciò che veramente hai bisogno è un archivio chiavi-valori supportato da SQLite. Vedi archiviazione chiavi-valori in Capacitor.- Un ORM o costruttore di query sopra: Kysely funziona con Fast SQL attraverso un breve driver personalizzato. Drizzle attualmente si adatta meglio al plugin della community.
La full Fast SQL API è in il plugin docs e sul pagina plugin.