Índice de documentación de SQLite3 WebAssembly
(sqlite.org)- Es un hub que reúne en un solo lugar la documentación de la API de WebAssembly y JavaScript para desarrolladores que quieran usar sqlite3 directamente en navegadores modernos con soporte WASM
- Para empezar rápido, se puede arrancar con el HOWTO de 3 pasos, y revisar las rutas de instalación y despliegue con la documentación de descargas, módulo npm, compilación y detalles de Emscripten
- La documentación de la API cubre métodos de carga, recetas de código cliente, almacenamiento persistente, estructuras C en la API de JS, y cómo crear tablas virtuales y funciones con valor de tabla
- También organiza casos de uso reales como SQLime, Evolu, SQLiteNext, sqlite-wasm-esm y sqlite-wasm-http, junto con proyectos previos como sql.js, wa-sqlite y absurd-js
- Conecta materiales sobre SQLite en el navegador, OPFS, IndexedDB y WASI para comparar rápidamente opciones de implementación al usar SQLite en entornos web
Alcance de la documentación de sqlite3 WASM
- Es la documentación de la API de WebAssembly y JavaScript del proyecto SQLite, y cubre las APIs que permiten usar sqlite3 en navegadores modernos con soporte WASM
-
Guía del proyecto y canales de soporte
- About the sqlite3 WASM subproject
- Project news
- FAQ: Frequently Asked Questions
- TODOs: cubre tanto funciones planificadas como no planificadas
- El soporte técnico y la retroalimentación se reciben en el SQLite forum, y quienes tengan commercial SQLite support contracts pueden usar sus canales de soporte existentes
-
Documentación de ejecución, instalación y compilación
- three-step HOWTO: muestra cómo incluir y ejecutar el módulo sqlite3 WASM y la API de JavaScript relacionada
- Downloads: disponible desde la página principal de descargas del proyecto
- Prerelease snapshots: se actualizan con frecuencia
- npm module
- Building sqlite3 WASM: cubre el procedimiento de compilación de sqlite3 WASM y el código JS relacionado
- Detalles de compilación con Emscripten
-
Documentación de la API y detalles de implementación
- The API index: explica varias variantes de la API y los métodos de carga y acceso
- Cookbook: recetas para código del lado cliente
- Persistent storage options: opciones de almacenamiento persistente
- C-structs: cómo usar estructuras C desde la API de JS
- virtual tables: cómo crear tablas virtuales y funciones con valor de tabla desde JS
- API changes: cambios de API que pueden afectar a los clientes
- module symbols app: genera una lista de símbolos de la API de sqlite3 en el módulo JavaScript/WASM
-
Documentación y mantenimiento del repositorio
Casos de uso y ecosistema relacionado
-
Proyectos de terceros que usan este proyecto
- SQLime: ofrece una interfaz de navegador de bases de datos
- Evolu: plataforma local-first enfocada en privacidad, facilidad de uso y ausencia de dependencia de proveedor
- SQLiteNext: demo para integrar este proyecto con next.js
- sqlite-wasm-esm: muestra cómo usar este proyecto con la toolchain de Vite
- sqlite-wasm-http: SQLite VFS que ofrece acceso de solo lectura a bases de datos servidas directamente por HTTP
-
Trabajo previo y proyectos cercanos
- sql.js de Alon Zakai: es conocido como el primer caso de uso directo de sqlite3 en el navegador, excluyendo WebSQL, y se remonta a un commit de 2012
- wa-sqlite de Roy Hashimoto: es conocido como la primera implementación de OPFS storage para bases de datos sqlite3, e incluye trabajo relacionado con SQLite VFS del lado de JS
- absurd-js de James Long: muestra una forma de almacenar una base de datos sqlite3 dentro de una base de datos IndexedDB
- postgres-wasm: ejecuta un Postgres database server en el navegador
- Jaccwabyt: pequeña librería JS para manipular con código JS estructuras C alojadas en WASM, creada para apoyar la implementación de OPFS sqlite3_vfs de este proyecto
- CoWasm: “Collaborative WebAssembly for Servers and Browsers”, e incluye una compilación WASM de la aplicación sqlite3 shell en su demo app
- absurder-js: proyecto en el que Nicholas G. Piesco replantea absurd-js, y también tiene una presentación en el SQLite forum
- sqlite-wasm-rs: ofrece bindings de SQLite en Rust para compilaciones
wasm32-unknown-unknown
-
Documentación externa y artículos
- Porting WebSQL to OPFS: artículo del equipo de desarrollo de Google Chrome sobre SQLite WASM
- building SQLite3 for WASI: artículo del equipo VMWare OCTO sobre compilar SQLite3 para WASI
1 comentarios
Opiniones de Hacker News
Sería interesante ejecutar SQLite en memoria en el navegador y transmitir una copia del log WAL al servidor, de forma similar a como lo hacen Litestream y Cloudflare Durable Objects
Se podría usar WebSocket, o también parece posible hacerlo con POST esporádicos mediante
fetch(). En visitas posteriores, se usarían los datos del lado del servidor para volver a poblar el SQLite del clienteSin embargo, viendo esta publicación del foro de SQLite, parece que el build WASM predeterminado de SQLite no incluye el modo WAL, así que haría falta un build personalizado
SQLite en memoria no usa WAL, y Wasm en el navegador no soporta cosas como la API de memoria compartida que espera el modo WAL de SQLite
Litestream requiere una configuración de WAL muy precisa, que por casualidad encaja bien en la configuración predeterminada de SQLite nativo, pero es difícil de reproducir en Wasm
Cloudflare Durable Objects quizá se haya inspirado en Litestream, pero en la práctica funciona de manera bastante distinta; lo mismo ocurre con LiteFS o Turso
La idea general de transmitir los cambios de SQLite sí es posible, pero implica bastante trabajo, y el modelo de concurrencia de Wasm en el navegador hace que la implementación sea complicada
Escribí ese post del foro hace tiempo y logré hacer funcionar WAL en builds de SQLite Wasm del lado del servidor, pero no era una opción utilizable ni viable para el navegador
justjs14Tendría que buscar el código de entonces, pero compilaba SQLite con un VFS a Wasm en el navegador, abría la DB, hacía cambios y permitía hacer push/pull del WAL al servidor. También era posible sincronizar entre navegadores, ya fuera manualmente o con WebRTC, y si le dabas un token de GitHub al cliente del navegador, funcionaba incluso en GitHub Pages
Si hay interés, podría rehacerlo desde cero para que vuelva a ejecutarse. En ese momento experimenté bastante con este enfoque y creo que puede ser útil al menos para algunas aplicaciones
También hay una demo solo con función de pull aquí: https://just.billywhizz.io/sqlite/demo/#https://just.billywhizz.io/sqlite/demo/db/chinook.db
La “memoria compartida” puede ser
SharedArrayBuffer, y si se abre una sola conexión a la DB a la vez, también puede ser un buffer normal. Por ejemplo, se puede hacer así dentro de unSharedWorker, que ya es algo bastante comúnLa implementación no sería simple, pero claramente es posible. De hecho, el problema más grande es que captura cambios a nivel de bloque, no a nivel de fila
Por eso sirve para replicar toda la DB, pero es difícil hacer sincronización parcial, como compartir solo algunas filas con otros usuarios
Si se necesitan cambios a nivel de fila para sincronización parcial, habría que usar triggers o algo como la extensión de sesiones de SQLite: https://sqlite.org/sessionintro.html
En PowerSync usamos triggers del lado del cliente, y no hemos encontrado grandes desventajas aparte del trabajo de mantenimiento de los triggers
Por ejemplo, si se ejecuta con Wasm el renderizado de plantillas comunes o algunos endpoints de Htmx, incluso un framework web tradicional del lado del servidor podría ofrecer funcionalidad offline con un almacenamiento SQLite Wasm similar a lo que menciona el OP
Ejemplo completo con Django: https://github.com/m-butterfield/django_webassembly
Ejemplo mínimo en Rust que separa cliente/servidor con macros: https://github.com/richardanaya/wasm-service
Clojure Electric hace algo parecido sin Wasm, usando más macros: https://github.com/hyperfiddle/electric
También hay una herramienta web reciente de Microsoft que empieza con B
En los últimos años, una cantidad sorprendentemente grande del ecosistema de Python obtuvo soporte para Wasm, así que se volvió utilizable para este enfoque. No solo las bibliotecas obviamente relacionadas con machine learning, sino también bibliotecas de juegos como Pygame y Pyxel; además, como SDL2 agregó soporte, parece que el soporte para Kivy, una biblioteca de UI multitouch, podría llegar pronto
Parece que se abren muchas posibilidades nuevas para ejecutar código en cualquier computadora y, si hace falta, evitar burocracias de plataforma o comisiones dentro del sandbox de un navegador web
La documentación dice que estos componentes aparecieron por primera vez como beta pública en la versión 3.40, y que la API se estabilizaría en la 3.41 según el feedback de la comunidad.
Pero la versión más reciente de SQLite es la 3.46.1 del 2024-08-13, así que probablemente la API ya se estabilizó, pero la documentación no se actualizó.
Creo que sería un buen medio de distribución si el equipo de SQLite sacara un paquete oficial de npm que incluyera la versión WASM. Vi después, según una respuesta, que ya existe.
Personalmente, mi SQLite-in-WASM favorito sigue siendo la variante de Pyodide. Existía desde mucho antes que la implementación oficial de SQLite, y si usas Pyodide, WASM SQLite viene incluido como parte de la biblioteca estándar de Python.
Así lo uso en https://lite.datasette.io/, y también se puede probar en https://pyodide.org/en/stable/console.html.
Si ejecutas
select sqlite_version()consqlite3.connect(':memory:'), devuelve la 3.39.0 del 2022-06-25, así que parece que Pyodide necesita actualizar la versión. Parece que esta versión la hereda de emscripten: https://github.com/emscripten-core/emscripten/blob/main/tools/ports/sqlite3.pySin embargo, el equipo de SQLite no va a sacar un paquete oficial de npm. Solo distribuimos JS puro, y nos negamos rotundamente a meternos en la madriguera de conejo que implica dar soporte a herramientas externas al lenguaje. Ningún miembro del proyecto usa esas herramientas.
Damos soporte a un build de npm “aprobado oficialmente” mantenido por Thomas Steiner, pero no apuntamos activamente a ningún framework de JS como objetivo de desarrollo.
Si damos soporte directo a un framework específico, incluido npm, puede dar la impresión de que avalamos ese framework, y evitamos activamente avalar proyectos de terceros.
@sqlite.org/sqlite-wasmconjsdelivry cargarsqlite3-bundler-friendly.mjsysqlite3.wasm.Con eso puedes usar la API en C de SQLite y la API orientada a objetos
oo1, y también funciona con una etiqueta de script normaltype=moduleo en Deno.Aquí hay un HTML de ejemplo: https://github.com/Ciantic/experimenting-sqlite-wasm/blob/main/standalone.html
sqlite-wasmcarga mucho más rápido que Pyodide, así que si no necesitas Python, la primera opción es mejor.Aunque parece bastante limitado. No tiene
sqlite_version(), así que ni siquiera pude verificar la versión, y la versión del repositorio es la 3.38.1, bastante antigua.Actualmente, la shell web de DuckDB no puede cargar la extensión de SQLite porque todavía no se ha lanzado la extensión de SQLite para la 1.1.2, que salió ayer. La versión anterior funciona con la edición WASM actualizada recientemente.
También se pueden agregar extensiones espaciales, GDAL, búsqueda vectorial, etc.: https://github.com/duckdb/duckdb-wasm/releases/tag/v1.29.0
Crear tu propia “shell web de SQL” tampoco fue muy difícil, pero la documentación no estaba lo suficientemente completa.
https://github.com/duckdb/sqlite_scanner/blob/main/src/sqlite/sqlite3.h
Para usarlo en Go, me gusta mucho el paquete Wasm SQLite de ncruces https://github.com/ncruces/go-sqlite3.
El paquete de Go de cznic también es excelente, pero esta versión Wasm funciona bien incluso en entornos como OpenBSD.
WebSQL simplemente debería haber sido SQLite, y si lo hubiera sido, el enfoque offline-first y el ecosistema general de almacenamiento de apps habrían estado mucho mejor.
¿Todavía habrá alguna posibilidad? En lugar de abstraer SQLite y sobrespecificarlo, ¿no podrían especificar la versión de la API de SQLite que los navegadores deben soportar y subir la versión periódicamente?
Una de las bases de la Web es que, en lo posible, lo existente debe seguir funcionando, y ese tipo de cambios incompatibles es difícil.
Aunque la especificación enumerara varias versiones y dejara que la página web eligiera, los navegadores cargarían con la complicada responsabilidad de mantener versiones antiguas de SQLite que quizá ya no tengan soporte. Además, cada versión ocupa alrededor de 1 MB.
Si es así, me parece mejor dejar que cada sitio web asuma el costo de su propia elección, como ahora.
Si la preocupación es descargar repetidamente recursos comunes, es un problema que otros CDN ya han aceptado. Una solución de caché compartido para dependencias comunes tendría mucho más valor que algo limitado solo a SQLite.
El enfoque actual me parece mejor que una versión provista por el navegador.
Ahora, cuando lo necesitas, puedes traer la versión más reciente de SQLite como un blob WASM comprimido de 410 KB. Si hubiéramos quedado atados a la versión de SQLite impuesta por el navegador, quizá ahora estaríamos usando una versión de hace 10 años.
Al hojear la página rápido, no me queda claro el caso de uso.
Más concretamente, ¿esto puede ser un reemplazo de IndexedDB? ¿Los datos persisten, o hay que guardar directamente el archivo SQLite en la File System API o en IndexedDB/localStorage?
Actualmente incluye almacenamiento mediante Origin-Private FileSystem (OPFS) y, aunque de forma muy limitada, backends de
window.localStorageywindow.sessionStorage.Últimamente me interesa mucho el ámbito local-first, pero comparado con usar las APIs de almacenamiento integradas del navegador, especialmente IndexedDB, embeber SQLite se siente bastante pesado.
Las principales bibliotecas open source también parecen usar, en general, las APIs integradas.
Me gustaría ver una solución open source que ofrezca una API similar a SQLite y sincronización sobre el almacenamiento nativo del navegador, en lugar de intentar meter otro ejecutable con WebAssembly.
WHEREen vez de filtros conif()también podría usar menos memoria y CPU.Si lo descargas desde la página oficial y eliminas los archivos
mjsy los archivos “bundler-friendly”, la dependencia mínima de SQLite Wasm queda en unos 1.3 MB.Para una app de navegador parece algo grande, pero hoy Wasm también se ejecuta en otros lugares, así que en esos entornos puede tener más sentido.
sqlite3.jsy el Wasm pesan 1.3 MB sin comprimir, pero si se minimiza el JS y se comprimen ambos archivos con Brotli, baja a 410 KB.Si solo vas a guardar preferencias de usuario, obviamente no hace falta descargar SQLite, pero si la tarea se beneficia de tener una base de datos completa, no tiene sentido preocuparse demasiado por 1 MB y terminar reinventando la rueda.
Si el otro comentario tiene razón, en la red ni siquiera serían 1.3 MB para empezar.
Aun así, en apps de navegador donde el código de la aplicación y los datos ya son grandes, como apps parecidas a Figma o Photoshop, o apps de machine learning, 1.3 MB quizá no sea una carga adicional importante.
También vale la pena considerar que el parseo de Wasm es bastante más rápido que el de JS. Ahora no encuentro la fuente de esa afirmación, pero había al menos un buen artículo relacionado.
https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
Puede ser local, en un servidor central o en el edge cercano.
Además, WASI todavía está lejos de ser la respuesta. La amalgamación de SQLite compila bien con WASI, pero la concurrencia sigue siendo un problema sin resolver.
Tuve que crear un VFS desde cero para dejar un driver de SQLite basado en Wasm en un estado utilizable.
https://github.com/ncruces/go-sqlite3/blob/main/vfs/README.md
Durante un tiempo intenté hacer que esto funcionara en el ecosistema Rust, pero parecía que los wrappers SQL de blessed.rs,
rusqliteysqlx, todavía no podían aprovecharlo.También era difícil envolverlo directamente, porque no encontré cómo hacer que el código Wasm de emscripten funcionara bien con
wasm32-unknown-unknown.Al final se necesita algún tipo de wrapper JS, y entonces hay que implementar en JS la interfaz que esperan esos crates y exponerla. Si esto se resuelve en Rust, sería realmente genial.
En esencia, hay que importar en
wasm-bindgenuna compilación emscripten de SQLite mediante headersextern C, compilarla en modo multihilo para permitir acceso a shared array buffers y reimplementar el VFS en Rust.Después se podría acceder a las filas de SQLite como bytes Wasm crudos y crear un wrapper o una integración al estilo Rust-SQLite.
Seguirían faltando comodidades como connection pooling, pero en Wasm probablemente convenga usar la DB en modo exclusivo.
Esta semana estoy preparando el release 0.2 y voy a pulir algunos problemas. Fuera de eso, se puede usar Diesel ORM prácticamente como en nativo.
emscriptenconwasm32-unknown-unknown.Hay un plan para que Emscripten y Rust funcionen bien juntos en Wasm: https://github.com/rustwasm/wasm-bindgen/pull/4014#issuecomment-2356908451
Creé una biblioteca SQLite que se ejecuta en una JVM pura usando la compilación Wasm de SQLite y el runtime Chicory: https://github.com/dylibso/sqlite-zero
Es más un experimento que algo con el objetivo de estar listo para producción, pero creo que puede ser útil para llevar una herramienta SQLite sin dependencias al ecosistema JVM
Pregunta general: ¿en qué situaciones conviene más enviar toda la DB y hacer que el navegador procese las consultas?
Dicho de otra forma, ¿cuándo se usaría esto para mejorar la experiencia de usuario frente al modelo tradicional de una DB alojada en el servidor?
Con el driver adecuado, se pueden transmitir por streaming los fragmentos necesarios de la base de datos en vez de enviar la base completa: https://github.com/mmomtchev/sqlite-wasm-http
Se puede hacer búsqueda de texto completo de SQLite sin descargar toda la base de datos FTS. Si el término de búsqueda es lo bastante corto, igual se descarga la mayor parte, pero no todo
https://medium.com/offline-camp/couchdb-pouchdb-and-hoodie-as-a-stack-for-progressive-web-apps-a6078a985f18
Puede ir desde bibliotecas de audio hasta software de modelado 3D. Se pueden crear cambios localmente, persistirlos y luego sincronizarlos con el servidor de forma periódica o cuando se vuelva a estar en línea
Este tipo de apps son buenas candidatas para una DB embebida del lado del cliente