← Blog

SQLite en el Navegador Está Subestimado

·
SQLiteWASMArquitectura

Cada vez que construyo una herramienta interna que necesita almacenamiento local, la sugerencia por defecto es IndexedDB. Y cada vez, termino usando SQLite compilado a WASM.

Acá explico por qué.

El Problema de IndexedDB

IndexedDB está bien para almacenamiento key-value. Pero en el momento que necesitás queries relacionales, se desmorona:

  • Sin SQL. Escribís código verboso basado en cursores para lo que debería ser un SELECT ... WHERE ... JOIN.
  • Las migraciones de esquema son dolorosas. Gestionás números de versión y escribís lógica de upgrade imperativa.
  • Sin exportación fácil. ¿Querés darle al usuario sus datos como archivo? Buena suerte serializando IndexedDB.
  • El rendimiento se degrada. Datasets grandes con queries complejas se vuelven notablemente lentos.

Para un caché simple o cola offline, IndexedDB está bien. Para una herramienta operativa donde los usuarios consultan, filtran y exportan datos? Es la abstracción incorrecta.

SQLite vía WASM

sql.js compila SQLite a WebAssembly. Obtenés una base de datos relacional real en el navegador:

SELECT c.name, COUNT(j.id) as job_count, SUM(j.total) as revenue
FROM clients c
JOIN jobs j ON j.client_id = c.id
WHERE j.status = 'completed'
GROUP BY c.id
ORDER BY revenue DESC

Intentá escribir eso con cursores de IndexedDB.

Los Tradeoffs

Tamaño del bundle: ~1MB para el binario WASM. Para un sitio de marketing público, es demasiado. Para una herramienta interna que se usa diariamente, es nada.

Persistencia: SQLite corre en memoria por defecto. Necesitás serializar la base de datos a localStorage, IndexedDB (irónico), o el Origin Private File System (OPFS) para persistencia. OPFS es la mejor opción en navegadores modernos.

Sin escrituras concurrentes: Una pestaña a la vez puede escribir. Para herramientas internas de un solo usuario, esto está bien. Para escenarios multi-pestaña, necesitás una estrategia de locking.

Cuándo Usarlo

Usá SQLite en el navegador cuando:

  • Tu app maneja muchos datos (cientos o miles de registros)
  • Los usuarios necesitan filtrar, buscar y agregar
  • Querés exportación/backup de un solo archivo
  • La app es una herramienta interna, no un sitio web público

Quedate con IndexedDB cuando:

  • Estás cacheando respuestas de API
  • Necesitás una cola offline simple
  • El tamaño del bundle es crítico
  • No necesitás queries relacionales

La Exportación Es la Funcionalidad Killer

Lo mejor de SQLite en el navegador: el usuario puede descargar toda su base de datos como un solo archivo.

Sin llamadas a API. Sin lógica de serialización. Solo db.export() y entregale un archivo .db que puede abrir con cualquier herramienta SQLite.

Para herramientas operativas donde importa la propiedad de los datos, esto es una señal de confianza enorme. El usuario sabe que no está atrapado.


¿Construyendo una herramienta interna con muchos datos? Hablemos sobre la arquitectura correcta.

¿Querés discutir esto?

Si esto resonó con un problema que estás enfrentando, hablemos.