Saltar al contenido principal

Alternativa a @capacitor-community/sqlite en 2026

¿Buscas una alternativa a @capacitor-community/sqlite? Compárala con Capgo Fast SQL y migra tu Capacitor SQLite code y tus datos de usuario paso a paso.

Logotipo de Capgo para una guía sobre alternativas al plugin SQLite de la comunidad de Capacitor

La principal alternativa a @capacitor-community/sqlite en Capacitor 8 es @capgo/capacitor-fast-sql: un plugin SQLite nativo para iOS, Android y web que mantiene SQLCipher opcional y envía los datos de las consultas a través de un canal HTTP local en lugar del bridge de Capacitor. Esta guía compara ambos de forma honesta, indica cuándo conviene quedarse con el plugin de la comunidad y explica cómo migrar tanto tu code como las bases de datos existentes de tus usuarios.

Por qué los equipos buscan una alternativa

@capacitor-community/sqlite ha sido el plugin SQLite predeterminado para Capacitor durante años y sigue manteniéndose (la versión 8.1.1 se lanzó en agosto de 2026). Las razones para cambiar son específicas:

  1. Serialización del bridge. Cada resultado pasa por el bridge de Capacitor como JSON. Eso está bien para consultas pequeñas, pero se nota cuando cargas miles de filas para sincronización o búsqueda.
  2. SQLCipher siempre está incluido. El plugin depende de SQLCipher en iOS y Android incluso si nunca encriptas. Eso aumenta el tamaño nativo y significa que tu binario contiene code de cifrado que debes tener en cuenta en las respuestas de cumplimiento de exportación.
  3. Modelo de almacenamiento web. En la web, utiliza el jeep-sqlite componente: sql.js en memoria, persistido en IndexedDB cuando llamas a saveToStore(). Debes recordar guardar, y toda la base de datos vive en memoria.
  4. Una gran superficie de API. Wrappers de conexión, comprobaciones de consistencia, conexiones de solo lectura y no conformes, tablas de sincronización, importación y exportación JSON. Potente, pero mucho que aprender para «ejecutar SQL».
  5. Una sola passphrase por app. La cifración usa un único secreto almacenado por el plugin (setEncryptionSecret), compartido por todas las bases de datos cifradas de la app.

Comparación lado a lado

@capacitor-community/sqlite 8.1 @capgo/capacitor-fast-sql 8.2
Plataformas iOS, Android, Web, Electron iOS, Android, Web
Ruta de datos bridge de Capacitor Servidor HTTP local con token por conexión (nativo)
Motor web jeep-sqlite (sql.js + IndexedDB, manual saveToStore) Oficial @sqlite.org/sqlite-wasm con OPFS, las escrituras van al archivo
Cifrado SQLCipher siempre incluido, una passphrase gestionada por el plugin SQLCipher opcional, clave pasada por base de datos en connect()
Swift Package Manager Sí (Package.swift) Sí (Package.swift)
Versionado de esquema upgradeStatements / addUpgradeStatement El tuyo, normalmente PRAGMA user_version
Importación/exportación JSON, sincronización de tablas Sí No
Base de datos precargada desde assets copyFromAssets() Copia el archivo por tu cuenta antes connect()
Helper de clave-valor No KeyValueStore
BLOBs Sí Sí, como Uint8Array
Soporte de ORM TypeORM integrado capacitor driver Drivers personalizados (Kysely funciona bien)
Configuración nativa adicional Web: copiar sql-wasm.wasm iOS NSAllowsLocalNetworking, regla cleartext de localhost en Android
Mantenedor comunidad de Capacitor Capgo

Cuándo seguir con el plugin de la comunidad

Sé práctico. Quédate si:

  • lanzas un Electron build,
  • usas TypeORM (su driver capacitor está escrito para el plugin de la comunidad, ver TypeORM con Capacitor),
  • dependes de importación/exportación JSON o tablas de sync y no quieres reconstruirlas,
  • tus queries son pequeñas y la app funciona bien hoy.

Cuándo Fast SQL es mejor opción

Migra si:

  • cargas o escribes datasets grandes (motores de sync, catálogos offline, índices de búsqueda, logs de CRDT),
  • quieres cifrar solo algunas bases de datos, con claves del Keychain o Keystore,
  • no quieres SQLCipher en una app que no lo necesita,
  • quieres un archivo SQLite real en la web a través de OPFS, en lugar de una copia en memoria,
  • quieres un API simple: conectar, consultar, ejecutar, transacción.

Para una guía completa de Fast SQL, consulta Cómo usar SQLite en una app de Capacitor.

Paso 1: instala Fast SQL junto al plugin anterior

Mantén el plugin de la comunidad instalado durante una versión para poder migrar los datos existentes.

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

Configuración nativa que necesita Fast SQL:

  • iOS: NSAppTransportSecurity > NSAllowsLocalNetworking = true en Info.plist.
  • Android: una network_security_config.xml que permite cleartext a localhost y 127.0.0.1, referenciado desde el manifiesto.
  • Cifrado: net.zetetic:sqlcipher-android en el módulo de la app de Android y el CapgoCapacitorFastSql/SQLCipher pod en iOS. Detalles en cifrando SQLite en Capacitor.
  • Web: Cross-Origin-Opener-Policy: same-origin y Cross-Origin-Embedder-Policy: require-corp headers para OPFS.

Paso 2: mapea el API

Abrir una base de datos

Antes:

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

Después:

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

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

FastSQL.connect() devuelve la conexión existente si la base de datos ya está abierta, así que no hay isConnection / retrieveConnection confusión.

Mapeo de métodos

Plugin de la comunidad SQL rápido
createConnection() + open() FastSQL.connect({ database })
closeConnection(name) FastSQL.disconnect(name)
db.query(sql, values) devuelve { values } db.query(sql, params) devuelve filas
db.run(sql, values) devuelve { changes: { changes, lastId } } db.run(sql, params) devuelve { rowsAffected, insertId }
db.execute(multiStatementString) Uno db.execute(sql) por sentencia, 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 ejecutor de migraciones
copyFromAssets() Copiar un bundle .db en Directory.Data antes de la primera connect()
setEncryptionSecret() + mode: 'secret' encrypted: true, encryptionKey en connect()
initWebStore() / saveToStore() No hace falta, OPFS persiste las escrituras

Queries y escrituras

Antes:

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;

Después:

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

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

Ambos plugins devuelven filas como objetos con claves por nombre de columna.

Ejecución de múltiples sentencias

La comunidad execute() acepta una cadena con varias sentencias separadas por ;. Fast SQL prepara una sentencia por llamada. Divide tu SQL o, mejor aún, mantén las sentencias como 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 })));

No dividas en ; con un enfoque ingenuo split(';') si alguna instrucción contiene un cuerpo de trigger o un literal de cadena con un punto y coma.

Actualizar sentencias a user_version

El plugin de la comunidad guarda la versión del esquema en PRAGMA user_version también. Si mantienes los mismos números de versión, las bases de datos de tus usuarios actuales ya están en la versión correcta tras la migración del archivo del paso 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}`);
    });
  }
}

Comprueba el user_version en una instalación existente antes de confiar en esto. Si no estás seguro, regístralo desde el antiguo plugin en tu versión actual.

Bases de datos prepobladas

Del plugin de la comunidad copyFromAssets() copia las bases de datos empaquetadas en el primer inicio. Con Fast SQL, coloca el archivo donde el plugin lo busca antes de conectar. Fast SQL almacena <name>.db en Documents en iOS y en la app files directorio de la app en Android, que son ambos Directory.Data en @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 });
}

Para archivos grandes, descárgalos por partes o envía un archivo comprimido y usa la copia nativa del plugin de archivos en lugar de base64 en JavaScript.

Paso 3: migra los datos de usuario existentes

Los usuarios que actualizan tu app tienen una base de datos creada por el plugin de la comunidad. Se encuentra en una ruta diferente con un nombre distinto:

Plugin de la comunidad SQL rápido
iOS Documents/<name>SQLite.db (por defecto iosDatabaseLocation) Documents/<name>.db
Android databases/<name>SQLite.db files/<name>.db

Opción A: copiar el archivo (bases de datos sin cifrar)

El más rápido, y conserva todo: índices, 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' });
}

Llámalo antes del primer FastSQL.connect({ database: name }). Notas:

  • getUrl() (iOS y Android) devuelve la URL del archivo de base de datos. Al pasar una file:// URL sin directory permite al plugin Filesystem leer desde cualquier ruta dentro de tu sandbox.
  • Cerrar la conexión y hacer checkpoint primero asegura que no queden escrituras recientes en un -wal archivo.
  • Conserva el archivo antiguo durante una release y elimínalo después con el delete() método una vez que tengas confianza. Luego, elimina el plugin de la comunidad.

Opción B: copiar filas en JavaScript (bases de datos cifradas o un cambio de esquema)

Cuando la fuente está cifrada con el secreto almacenado del plugin de la comunidad, la ruta más simple y segura es dejar que ese plugin descifre y copie las filas a una nueva base de datos Fast SQL. Esto también permite cifrar con una nueva clave por base de datos desde el almacenamiento seguro.

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' y false en createConnection si la fuente no está cifrada. El plugin de la comunidad requiere iosIsEncryption / androidIsEncryption en capacitor.config por sus modos cifrados, que ya tienes si los usaste. Para tablas grandes, usa paginación con WHERE rowid > ? ORDER BY rowid LIMIT 5000.

Ambos plugins usan los valores por defecto de SQLCipher 4, así que un archivo cifrado también puede abrirse en Fast SQL directamente con la misma passphrase. Sin embargo, el plugin de la comunidad gestiona esa passphrase internamente, y la copia de filas evita depender de ese detalle.

Paso 4: eliminar el plugin antiguo

Después de uno o dos lanzamientos con la migración implementada:

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

Eliminar sql-wasm.wasm de tus assets y cualquier jeep-sqlite configuración en main.ts. Eliminar el plugin también elimina su dependencia de SQLCipher, por lo que si no optas por el cifrado de Fast SQL, los binarios de iOS y Android serán más pequeños.

La migración de datos code es JavaScript, así que puede enviarse con actualizaciones Live Update de Capgo, pero el release que añade Fast SQL es un cambio nativo y requiere un build para la tienda. Envía primero el build nativo. Build de Capgo puede generarlo en la nube si no tienes un Mac a mano (las subidas a App Store requieren Xcode 26 o posterior desde abril de 2026).

Solución de problemas

Fast SQL se conecta, pero todas las consultas fallan en iOS. NSAllowsLocalNetworking is missing from the app’s Info.plist, o la app no se recompiló después de añadirlo.

Android: CLEARTEXT communication to localhost not permitted. Añade el localhost domain-config a tu configuración de seguridad de red. Si ya tenías una para SSL pinning, combínalas.

Solo se creó parte de mi esquema. Pasaste una cadena con múltiples sentencias a execute(). Divídela en un array.

INSERT ... RETURNING no devuelve nada en Android. En Fast SQL 8.2, Android devuelve filas solo para sentencias que comienzan con SELECT, PRAGMA o EXPLAIN. Usa insertId de los run().

Los booleanos no coinciden en las lecturas de Android. Pasa 1/0 en lugar de true/false en los parámetros de consulta. Android enlaza los parámetros de consulta de lectura como texto.

La base de datos copiada está vacía. El plugin de la comunidad se configuró con un iosDatabaseLocation, o el nombre de origen es diferente. Registra la URL devuelta por getUrl().

Los datos web del plugin antiguo se han perdido. El plugin de la comunidad guardaba datos web en IndexedDB mediante jeep-sqlite, Fast SQL usa OPFS. Resincroniza los clientes web desde tu backend, o exporta con el plugin antiguo exportToJson e importa las filas en Fast SQL.

Otras opciones

  • @capgo/capacitor-data-storage-sqlite si lo que realmente necesitas es un almacén de clave-valor basado en SQLite. Mira el almacenamiento clave-valor en Capacitor.
  • Un ORM o query builder encima de: Kysely funciona con Fast SQL mediante un pequeño driver personalizado. Drizzle se adapta mejor al plugin de la comunidad actualmente.

La API completa de Fast SQL está en la docs del plugin y en la página del plugin.

Live updates para apps Capacitor

Cuando un bug de la capa web está en producción, publica el fix a través de Capgo en lugar de esperar días a la aprobación de la app store. Los usuarios reciben la actualización en segundo plano mientras los cambios nativos siguen el flujo de revisión habitual.

Lo último de nuestro blog

Capgo te da la información clave que necesitas para crear una app móvil realmente profesional.