Alternatif utama untuk @capacitor-community/sqlite di Capacitor 8 adalah @capgo/capacitor-fast-sql: plugin SQLite native untuk iOS, Android, dan web yang menjaga SQLCipher tetap opsional dan mengirim data query lewat kanal HTTP lokal, bukan lewat bridge Capacitor. Panduan ini membandingkan keduanya secara jujur, menjelaskan kapan sebaiknya tetap pakai plugin komunitas, dan memandu migrasi code maupun database pengguna yang sudah ada.
Mengapa tim mencari alternatif
@capacitor-community/sqlite telah menjadi plugin SQLite default untuk Capacitor selama bertahun-tahun dan masih dikelola (8.1.1 rilis Agustus 2026). Alasan orang berpindah sangat spesifik:
- Serialisasi bridge. Setiap hasil melewati bridge Capacitor sebagai JSON. Itu tidak masalah untuk query kecil, tapi terasa saat memuat ribuan baris untuk sinkronisasi atau pencarian.
- SQLCipher selalu disertakan. Plugin ini bergantung pada SQLCipher di iOS dan Android meskipun Anda tidak pernah mengenkripsi. Itu menambah ukuran native dan berarti binary Anda berisi code enkripsi yang harus Anda perhitungkan dalam jawaban kepatuhan ekspor.
- Model penyimpanan web. Di web, plugin ini menggunakan
jeep-sqlitekomponen: sql.js di memori, disimpan ke IndexedDB saat Anda memanggilsaveToStore(). Anda harus ingat untuk menyimpannya, dan seluruh database berada di memori. - Cakupan API yang besar. Connection wrappers, consistency checks, koneksi read-only dan non-conformed, sync tables, import dan export JSON. Powerful, tapi banyak yang harus dipelajari untuk sekadar “run SQL”.
- Satu passphrase per app. Enkripsi menggunakan satu secret yang disimpan oleh plugin (
setEncryptionSecret), yang dibagikan ke semua database terenkripsi dalam app.
Perbandingan berdampingan
@capacitor-community/sqlite 8.1 |
@capgo/capacitor-fast-sql 8.2 |
|
|---|---|---|
| Platform | iOS, Android, Web, Electron | iOS, Android, Web |
| Path data | bridge Capacitor | Server HTTP lokal dengan token per koneksi (native) |
| Mesin web | jeep-sqlite (sql.js + IndexedDB, manual saveToStore) |
Resmi @sqlite.org/sqlite-wasm dengan OPFS, penulisan diarahkan ke file |
| Enkripsi | SQLCipher selalu dibundel, satu passphrase yang dikelola plugin | SQLCipher opsional, kunci dilewatkan per database saat aktif connect() |
| Swift Package Manager | Ya (Package.swift) |
Ya (Package.swift) |
| Schema versioning | upgradeStatements / addUpgradeStatement |
Milik Anda sendiri, biasanya PRAGMA user_version |
| Impor/ekspor JSON, sinkronisasi tabel | Ya | Tidak |
| Database terisi awal dari aset | copyFromAssets() |
Salin file Anda sendiri sebelumnya connect() |
| Helper key-value | Tidak | KeyValueStore |
| BLOB | Ya | Ya, sebagai Uint8Array |
| Dukungan ORM | TypeORM bawaan capacitor driver |
Driver kustom (Kysely bekerja dengan baik) |
| Setup native tambahan | Web: salin sql-wasm.wasm |
iOS NSAllowsLocalNetworking, aturan cleartext localhost Android |
| Maintainer | komunitas Capacitor | Capgo |
Kapan sebaiknya tetap pakai plugin komunitas
Jadilah praktis. Tetap gunakan jika:
- Anda merilis Electron build,
- you use TypeORM (driver capacitor-nya ditulis untuk plugin komunitas, lihat TypeORM dengan Capacitor),
- yang kamu andalkan import/export JSON atau sinkronisasi tabel dan tidak ingin membangun ulang aplikasi tersebut,
- query Anda kecil dan aplikasi berjalan baik saat ini.
Kapan Fast SQL lebih cocok
Pindah jika:
- Anda memuat atau menulis dataset besar (sync engine, katalog offline, indeks pencarian, log CRDT),
- Anda ingin enkripsi hanya untuk beberapa database, dengan kunci dari Keychain atau Keystore,
- Anda tidak perlu menggunakan SQLCipher pada aplikasi yang tidak membutuhkannya.
- Anda ingin file SQLite asli di web melalui OPFS, bukan salinan in-memory,
- Anda ingin API yang sederhana: connect, query, run, transaction.
Untuk walkthrough lengkap tentang Fast SQL itu sendiri, lihat Cara menggunakan SQLite di aplikasi Capacitor.
Langkah 1: instal Fast SQL berdampingan dengan plugin lama
Biarkan plugin komunitas tetap terpasang untuk satu rilis agar Anda dapat memigrasikan data yang sudah ada.
bun add @capgo/capacitor-fast-sql
bunx cap sync
Konfigurasi native yang dibutuhkan Fast SQL:
- iOS:
NSAppTransportSecurity>NSAllowsLocalNetworking=truediInfo.plist. - Android:
network_security_config.xmlyang mengizinkan cleartext kelocalhostdan127.0.0.1, yang dirujuk dari manifest. - Enkripsi:
net.zetetic:sqlcipher-androiddi modul aplikasi Android danCapgoCapacitorFastSql/SQLCipherpod di iOS. Detail di enkripsi SQLite di Capacitor. - Web:
Cross-Origin-Opener-Policy: same-origindanCross-Origin-Embedder-Policy: require-corpheaders untuk OPFS.
Langkah 2: petakan API
Membuka database
Sebelum:
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();
Sesudah:
import { FastSQL } from '@capgo/capacitor-fast-sql';
const db = await FastSQL.connect({ database: 'app', walMode: true, performancePresets: true });
FastSQL.connect() mengembalikan koneksi yang ada jika database sudah terbuka, jadi tidak perlu isConnection / retrieveConnection ribet.
Pemetaan method
| Plugin komunitas | SQL Cepat |
|---|---|
createConnection() + open() |
FastSQL.connect({ database }) |
closeConnection(name) |
FastSQL.disconnect(name) |
db.query(sql, values) mengembalikan { values } |
db.query(sql, params) mengembalikan baris |
db.run(sql, values) mengembalikan { changes: { changes, lastId } } |
db.run(sql, params) mengembalikan { rowsAffected, insertId } |
db.execute(multiStatementString) |
Satu db.execute(sql) per statement, atau db.executeBatch([...]) |
db.executeSet(set) |
db.executeBatch(ops) dengan { statement, params } |
beginTransaction / commitTransaction / rollbackTransaction |
db.transaction(async (tx) => ...) atau beginTransaction() / commit() / rollback() |
addUpgradeStatement / upgradeStatements |
Sebuah PRAGMA user_version migration runner |
copyFromAssets() |
Salin bundle .db ke dalam Directory.Data sebelum pertama kali connect() |
setEncryptionSecret() + mode: 'secret' |
encrypted: true, encryptionKey aktif connect() |
initWebStore() / saveToStore() |
Tidak perlu, OPFS mempertahankan penulisan |
Query dan penulisan
Sebelum:
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;
Sesudah:
const notes = await db.query('SELECT * FROM notes WHERE pinned = ?', [1]);
const { insertId } = await db.run('INSERT INTO notes (title) VALUES (?)', ['Hello']);
Kedua plugin mengembalikan baris sebagai objek yang dikey berdasarkan nama kolom.
Eksekusi multi-statement
The community execute() menerima string dengan beberapa statement yang dipisahkan oleh ;. Fast SQL menyiapkan satu statement per panggilan. Pisahkan SQL Anda, atau lebih baik lagi, simpan statement sebagai 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 })));
Jangan pisahkan berdasarkan ; dengan cara naif split(';') jika ada statement yang berisi body trigger atau string literal dengan titik koma.
Upgrade statement ke user_version
Plugin komunitas menyimpan versi schema di PRAGMA user_version juga. Jika Anda tetap memakai nomor versi yang sama, database pengguna yang sudah ada sudah berada di versi yang benar setelah migrasi file di langkah 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}`);
});
}
}
Periksa nilai aktual user_version pada instalasi yang sudah ada sebelum mengandalkan ini. Log nilainya dari plugin lama di rilis Anda saat ini jika Anda tidak yakin.
Database yang sudah terisi
milik plugin komunitas copyFromAssets() menyalin database yang dibundel saat peluncuran pertama. Dengan Fast SQL, letakkan file di tempat yang dicari plugin sebelum terhubung. Fast SQL menyimpan <name>.db di Documents di iOS dan di direktori aplikasi files directory on Android, which are both Directory.Data di @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 });
}
Untuk file besar, unduh dalam chunk atau kirim file terkompresi dan gunakan native copy dari file plugin alih-alih base64 di JavaScript.
Langkah 3: migrasi data pengguna yang ada
Pengguna yang memperbarui aplikasi Anda memiliki database yang dibuat oleh plugin community. Database ini berada di path yang berbeda dengan nama yang berbeda:
| Plugin komunitas | SQL Cepat | |
|---|---|---|
| iOS | Documents/<name>SQLite.db (default iosDatabaseLocation) |
Documents/<name>.db |
| Android | databases/<name>SQLite.db |
files/<name>.db |
Opsi A: salin file (database tanpa enkripsi)
Tercepat, dan mempertahankan semuanya: index, 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' });
}
Panggil sebelum pertama kali FastSQL.connect({ database: name }). Catatan:
getUrl()(iOS dan Android) mengembalikan URL file database. Mengirimkanfile://URL lengkap tanpadirectorymemungkinkan plugin Filesystem membaca dari jalur apa pun di dalam sandbox Anda.- Menutup koneksi dan melakukan checkpoint terlebih dahulu memastikan tidak ada penulisan terbaru yang tertinggal di
-walAnda. - Simpan file lama untuk satu rilis, lalu hapus nanti dengan koneksi komunitas
delete()metode koneksi komunitas setelah Anda yakin. Lalu hapus plugin komunitas.
Opsi B: salin baris di JavaScript (database terenkripsi, atau perubahan skema)
Ketika sumber dienkripsi dengan secret yang disimpan oleh plugin komunitas, jalur aman paling sederhana adalah membiarkan plugin tersebut mendekripsi dan menyalin baris ke database Fast SQL baru. Ini juga memungkinkan Anda mengenkripsi dengan kunci per-database baru dari 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);
}
}
Gunakan 'no-encryption' dan false di createConnection jika sumber tidak dienkripsi. Plugin komunitas membutuhkan iosIsEncryption / androidIsEncryption di capacitor.config untuk mode terenkripsinya, yang sudah Anda miliki jika pernah menggunakannya. Untuk tabel besar, lakukan paging dengan WHERE rowid > ? ORDER BY rowid LIMIT 5000.
Kedua plugin menggunakan default SQLCipher 4, sehingga file terenkripsi juga bisa dibuka langsung di Fast SQL dengan passphrase yang sama. Namun, plugin komunitas mengelola passphrase tersebut secara internal, dan menyalin baris menghindari ketergantungan pada detail itu.
Langkah 4: hapus plugin lama
Setelah satu atau dua rilis dengan migrasi yang sudah diterapkan:
bun remove @capacitor-community/sqlite jeep-sqlite
bunx cap sync
Hapus sql-wasm.wasm dari aset Anda dan jeep-sqlite setup dalam main.ts. Menghapus plugin juga menghapus dependensi SQLCipher-nya, jadi jika Anda tidak mengaktifkan enkripsi Fast SQL, biner iOS dan Android menjadi lebih kecil.
Migrasi data code adalah JavaScript, jadi bisa dikirim bersama Live updates Capgo, tetapi rilis yang menambahkan Fast SQL adalah perubahan native dan membutuhkan build store. Kirim build native terlebih dahulu. Build Capgo dapat menghasilkannya di cloud jika Anda tidak memiliki Mac (upload App Store membutuhkan Xcode 26 atau lebih baru sejak April 2026).
Pemecahan Masalah
Fast SQL terhubung tetapi setiap query gagal di iOS. NSAllowsLocalNetworking tidak ada di aplikasi Info.plist, atau aplikasi tidak di-rebuild setelah menambahkannya.
Android: CLEARTEXT communication to localhost not permitted. Tambahkan localhost domain-config ke konfigurasi keamanan jaringan Anda. Jika sudah ada untuk SSL pinning, gabungkan keduanya.
Hanya sebagian skema saya yang dibuat. Anda mengirim string multi-statement ke execute(). Pecah menjadi array.
INSERT ... RETURNING tidak mengembalikan apa pun di Android. Di Fast SQL 8.2, Android hanya mengembalikan baris untuk statement yang diawali dengan SELECT, PRAGMA atau EXPLAIN. Gunakan insertId dari run().
Boolean tidak cocok saat dibaca di Android. Gunakan 1/0 alih-alih true/false di parameter query. Android mengikat parameter read-query sebagai teks.
Database yang disalin kosong. Plugin komunitas dikonfigurasi dengan custom iosDatabaseLocation, atau nama sumber berbeda. Log URL yang dikembalikan oleh getUrl().
Data web dari plugin lama hilang. Plugin komunitas menyimpan data web di IndexedDB melalui jeep-sqlite, Fast SQL menggunakan OPFS. Sinkronkan ulang klien web dari backend Anda, atau ekspor dengan exportToJson dari plugin lama dan impor baris ke Fast SQL.
Opsi lainnya
@capgo/capacitor-data-storage-sqlitejika yang benar-benar Anda butuhkan adalah penyimpanan key-value berbasis string yang didukung SQLite. Lihat penyimpanan key-value di Capacitor.- ORM atau query builder di atasnya: Kysely berfungsi dengan Fast SQL melalui driver kustom singkat. Drizzle saat ini lebih cocok dengan plugin komunitas.
Fast SQL lengkap API ada di dokumentasi plugin dan di halaman plugin.