Index de la documentation WebAssembly de SQLite3
(sqlite.org)- Un hub qui rassemble en un seul endroit la documentation WebAssembly et API JavaScript pour les développeurs qui veulent utiliser directement sqlite3 dans les navigateurs modernes prenant en charge WASM
- Pour une prise en main rapide, commencez par le HOWTO en 3 étapes ; les documents sur le téléchargement, le module npm, la compilation et les détails d’Emscripten permettent de vérifier les chemins d’installation et de déploiement
- La documentation de l’API couvre les modes de chargement, les recettes de code client, le stockage persistant, les structures C dans l’API JS, jusqu’à l’écriture de tables virtuelles et de fonctions à valeur de table
- Elle recense aussi des cas d’usage réels comme SQLime, Evolu, SQLiteNext, sqlite-wasm-esm et sqlite-wasm-http, ainsi que des projets précurseurs tels que sql.js, wa-sqlite et absurd-js
- Des ressources liées à SQLite dans le navigateur, OPFS, IndexedDB et WASI sont reliées afin de comparer rapidement les options d’implémentation pour utiliser SQLite dans un environnement web
Périmètre de la documentation WASM de sqlite3
- Il s’agit de la documentation WebAssembly et API JavaScript du projet SQLite ; elle couvre l’API permettant d’utiliser sqlite3 dans les navigateurs modernes prenant en charge WASM
-
Présentation du projet et voies de support
- About the sqlite3 WASM subproject
- Project news
- FAQ: Frequently Asked Questions
- TODOs : couvre à la fois les fonctionnalités planifiées et non planifiées
- Le support technique et les retours passent par le forum SQLite ; les détenteurs de contrats de support commercial SQLite peuvent utiliser leurs canaux de support habituels
-
Documentation d’exécution, d’installation et de compilation
- three-step HOWTO : montre comment inclure et exécuter le module WASM sqlite3 et l’API JavaScript associée
- Downloads : fourni depuis la page de téléchargement du projet principal
- Prerelease snapshots : mis à jour fréquemment
- npm module
- Building sqlite3 WASM : couvre la procédure de compilation de sqlite3 WASM et du code JS associé
- Détails de compilation Emscripten
-
Documentation sur l’API et les détails d’implémentation
- The API index : décrit les différentes variantes d’API ainsi que les modes de chargement et d’accès
- Cookbook : recettes pour le code côté client
- Persistent storage options : options de stockage persistant
- C-structs : comment utiliser des structures C depuis l’API JS
- virtual tables : comment créer des tables virtuelles et des fonctions à valeur de table en JS
- API changes : changements d’API susceptibles d’affecter les clients
- module symbols app : génère la liste des symboles de l’API sqlite3 dans le module JavaScript/WASM
-
Gestion de la documentation et du dépôt
Cas d’usage et écosystème lié
-
Projets tiers utilisant ce projet
- SQLime : fournit une interface de navigateur de base de données
- Evolu : plateforme local-first axée sur la confidentialité, la facilité d’utilisation et l’absence de dépendance à un fournisseur
- SQLiteNext : démonstration d’intégration de ce projet avec next.js
- sqlite-wasm-esm : montre comment utiliser ce projet avec la toolchain Vite
- sqlite-wasm-http : VFS SQLite fournissant un accès en lecture seule à une base de données servie directement via HTTP
-
Travaux précurseurs et projets adjacents
- sql.js d’Alon Zakai : connu comme le premier cas d’utilisation directe de sqlite3 dans le navigateur, à l’exception de WebSQL, et remontant à un commit de 2012
- wa-sqlite de Roy Hashimoto : connu comme la première implémentation du stockage OPFS pour une base de données sqlite3, avec également des travaux liés au VFS SQLite côté JS
- absurd-js de James Long : montre une approche consistant à stocker une base de données sqlite3 dans une base IndexedDB
- postgres-wasm : exécute un serveur de base de données Postgres dans le navigateur
- Jaccwabyt : petite bibliothèque JS permettant de manipuler depuis du code JS des structures C hébergées dans WASM, créée pour aider à l’implémentation OPFS sqlite3_vfs de ce projet
- CoWasm : « Collaborative WebAssembly for Servers and Browsers », dont la demo app inclut un build WASM de l’application shell sqlite3
- absurder-js : projet dans lequel Nicholas G. Piesco réimagine absurd-js, avec aussi une annonce sur le forum SQLite
- sqlite-wasm-rs : fournit des bindings Rust SQLite pour les builds
wasm32-unknown-unknown
-
Documentation et articles externes
- Porting WebSQL to OPFS : article de l’équipe Google Chrome dev à propos de SQLite WASM
- building SQLite3 for WASI : article de l’équipe VMWare OCTO sur la compilation de SQLite3 pour WASI
1 commentaires
Avis sur Hacker News
Ce serait intéressant de faire tourner SQLite en mémoire dans le navigateur et de streamer une copie du journal WAL vers un serveur, un peu comme le font Litestream et Cloudflare Durable Objects
On pourrait utiliser WebSocket, ou même des POST
fetch()intermittents. Lors des visites suivantes, les données côté serveur serviraient ensuite à réhydrater le SQLite côté clientCela dit, d’après ce billet du forum SQLite, le build SQLite WASM par défaut n’inclut pas le mode WAL, donc il faudrait probablement un build personnalisé
SQLite en mémoire n’utilise pas WAL, et le Wasm dans le navigateur ne prend pas en charge des choses comme l’API de mémoire partagée attendue par le mode WAL de SQLite
Litestream exige une configuration WAL très précise, qui se trouve fonctionner avec la configuration native par défaut de SQLite, mais qui est difficile à reproduire en Wasm
Cloudflare Durable Objects a peut-être été inspiré par Litestream, mais son fonctionnement réel est assez différent, et c’est aussi le cas de LiteFS ou Turso
L’idée générale de streamer les changements SQLite est possible, mais représente beaucoup de travail, et le modèle de concurrence du Wasm dans le navigateur rend l’implémentation délicate
J’avais écrit ce billet de forum à l’époque et j’ai bien fait fonctionner WAL dans des builds Wasm SQLite côté serveur, mais ce n’était ni une option vraiment utilisable ni réellement viable dans le navigateur
justjs14Il faudrait que je retrouve le code de l’époque, mais il compilait SQLite avec un VFS en Wasm dans le navigateur, ouvrait la DB, permettait de la modifier, puis de pousser/tirer le WAL vers un serveur. La synchronisation entre navigateurs était possible, manuellement ou via WebRTC, et cela fonctionnait même sur GitHub Pages si l’on donnait un token GitHub au client navigateur
Si ça intéresse quelqu’un, je peux essayer de le remettre en état de marche depuis zéro. J’ai beaucoup expérimenté avec cette approche à l’époque, et je pense qu’elle peut être utile au moins pour certaines applications
Il y a aussi une démo avec uniquement la fonction de pull ici : https://just.billywhizz.io/sqlite/demo/#https://just.billywhizz.io/sqlite/demo/db/chinook.db
La « mémoire partagée » peut être un
SharedArrayBuffer, et si l’on n’ouvre qu’une seule connexion à la DB à la fois, un buffer classique peut aussi suffire. Par exemple, on peut déjà faire cela dans unSharedWorker, ce qui est assez courantL’implémentation ne serait pas simple, mais elle est clairement possible. En pratique, le plus gros problème est qu’on capture des changements au niveau des blocs, pas au niveau des lignes
C’est donc utilisable pour répliquer toute la DB, mais difficile pour une synchronisation partielle du type partager seulement certaines lignes avec d’autres utilisateurs
Si l’on a besoin de changements au niveau des lignes pour une synchronisation partielle, il faut utiliser des triggers ou quelque chose comme l’extension sessions de SQLite : https://sqlite.org/sessionintro.html
Chez PowerSync, nous utilisons des triggers côté client, et à part le travail de maintenance des triggers, nous n’avons pas trouvé de gros inconvénient
Par exemple, si l’on fait tourner le rendu de templates classique ou certains endpoints Htmx en Wasm, même un framework web côté serveur classique pourrait fournir des fonctionnalités hors ligne avec un stockage SQLite Wasm similaire à ce que décrit l’OP
Exemple Django complet : https://github.com/m-butterfield/django_webassembly
Exemple Rust minimal avec macros pour séparer client/serveur : https://github.com/richardanaya/wasm-service
Clojure Electric fait quelque chose de similaire sans Wasm, avec davantage de macros : https://github.com/hyperfiddle/electric
Il y a aussi un outil web récent de Microsoft dont le nom commence par B
Ces dernières années, une partie étonnamment large de l’écosystème Python a gagné une prise en charge de Wasm, ce qui la rend exploitable pour cette approche. Pas seulement les bibliothèques clairement liées au machine learning, mais aussi des bibliothèques de jeux comme Pygame et Pyxel ; et comme SDL2 a ajouté ce support, la prise en charge de Kivy, une bibliothèque d’UI multitouch, semble également envisageable à court terme
On voit apparaître beaucoup de nouvelles possibilités pour exécuter du code sur n’importe quel ordinateur et, si nécessaire, dans le sandbox d’un navigateur web afin d’éviter la bureaucratie des plateformes et leurs commissions
La documentation indique que ces composants sont apparus pour la première fois en bêta publique dans la version 3.40, et que l’API devait être stabilisée dans la 3.41 en fonction des retours de la communauté.
Or la dernière version de SQLite est la 3.46.1 du 13/08/2024 ; l’API est donc probablement déjà stabilisée, mais la documentation ne semble pas avoir été mise à jour.
Ce serait un bon moyen de distribution si l’équipe SQLite publiait un package npm officiel regroupant la version WASM. En regardant plus tard, d’après les réponses, il en existe déjà un.
Personnellement, ma version SQLite-in-WASM préférée reste la variante Pyodide. Elle existe depuis bien plus longtemps que l’implémentation officielle de SQLite, et avec Pyodide, SQLite en WASM est fourni comme une partie de la bibliothèque standard Python.
C’est utilisé ainsi sur https://lite.datasette.io/, et on peut aussi l’essayer sur https://pyodide.org/en/stable/console.html.
Si l’on exécute
select sqlite_version()avecsqlite3.connect(':memory:'), on obtient la version 3.39.0 du 25/06/2022 ; Pyodide semble donc avoir besoin d’une mise à jour. Cette version semble héritée d’emscripten : https://github.com/emscripten-core/emscripten/blob/main/tools/ports/sqlite3.pyEn revanche, l’équipe SQLite ne publiera pas de package npm officiel. Nous ne distribuons que du JS pur, et nous refusons fermement de nous engager dans le terrier du support d’outils extérieurs au langage. Aucun membre du projet n’utilise ce genre d’outils.
Nous prenons en charge le build npm « officiellement approuvé » maintenu par Thomas Steiner, mais nous ne ciblons activement aucun framework JS dans le développement.
Prendre directement en charge un framework particulier, y compris npm, pourrait donner l’impression que nous le cautionnons, et nous évitons activement toute caution de projets tiers.
@sqlite.org/sqlite-wasmdepuisjsdelivrdans un module JS, puis de chargersqlite3-bundler-friendly.mjsetsqlite3.wasm.On peut alors utiliser l’API C de SQLite ainsi que l’API orientée objet
oo1, et cela fonctionne aussi avec une balise script classiquetype=moduleou avec Deno.Un exemple HTML est disponible ici : https://github.com/Ciantic/experimenting-sqlite-wasm/blob/main/standalone.html
sqlite-wasmse charge beaucoup plus vite que Pyodide ; si vous n’avez pas besoin de Python, c’est donc le meilleur choix.Cela dit, cela semble assez limité. Comme il n’y a pas de
sqlite_version(), je n’ai même pas pu vérifier la version, et celle présente dans le dépôt est la 3.38.1, qui commence à dater.Le shell web actuel de DuckDB ne peut pas charger l’extension SQLite, car celle destinée à la version 1.1.2 sortie hier n’a pas encore été publiée. Les versions précédentes fonctionnent avec l’édition WASM récemment mise à jour.
On peut aussi y ajouter l’extension spatiale, GDAL, la recherche vectorielle, etc. : https://github.com/duckdb/duckdb-wasm/releases/tag/v1.29.0
Créer soi-même un « shell web SQL » n’était pas très difficile, mais la documentation n’était pas suffisamment complète.
https://github.com/duckdb/sqlite_scanner/blob/main/src/sqlite/sqlite3.h
Pour une utilisation avec Go, j’aime vraiment beaucoup le package Wasm SQLite de ncruces : https://github.com/ncruces/go-sqlite3
Le package Go de cznic est également excellent, mais cette version Wasm fonctionne bien même dans des environnements comme OpenBSD.
WebSQL aurait simplement dû être SQLite ; si cela avait été le cas, l’écosystème offline-first et, plus généralement, celui du stockage applicatif auraient été bien meilleurs.
Y a-t-il encore une possibilité aujourd’hui ? Plutôt que d’abstraire SQLite et de surspécifier les choses, ne pourrait-on pas spécifier la version de l’API SQLite que les navigateurs doivent prendre en charge, puis relever régulièrement cette version ?
L’un des principes fondamentaux du Web est que l’existant doit continuer à fonctionner autant que possible, et ce genre de changement cassant est difficile.
Même si la spécification listait plusieurs versions et laissait les pages web choisir, les navigateurs se retrouveraient avec la lourde responsabilité de conserver d’anciennes versions de SQLite qui pourraient ne plus être prises en charge. Chaque version occupe en plus environ 1 Mo.
Dans ce cas, il me semble préférable de laisser chaque site web assumer le coût de son propre choix, comme aujourd’hui.
Si le téléchargement répété de ressources communes vous inquiète, c’est un problème que d’autres CDN acceptent déjà. Une solution de cache partagé pour les dépendances communes aurait bien plus de valeur qu’une solution limitée à SQLite.
L’approche actuelle me semble meilleure qu’une version fournie par le navigateur.
Aujourd’hui, on peut récupérer la dernière version de SQLite sous forme de blob WASM compressé de 410 Ko quand on en a besoin. Si nous avions été liés à une version de SQLite imposée par les navigateurs, nous utiliserions peut-être aujourd’hui une version vieille de 10 ans.
En parcourant rapidement la page, le cas d’usage n’est pas très clair
Plus précisément, est-ce que cela peut servir de remplaçant à IndexedDB ? Les données sont-elles persistées, ou faut-il stocker directement le fichier SQLite dans la File System API ou dans IndexedDB/localStorage ?
Pour l’instant, cela inclut le stockage via Origin-Private FileSystem (OPFS) et, de façon très limitée, via les backends
window.localStorageetwindow.sessionStorageJe m’intéresse beaucoup ces derniers temps au domaine local-first, mais embarquer SQLite me paraît assez lourd comparé à l’utilisation des API de stockage intégrées au navigateur, en particulier IndexedDB
Les principales bibliothèques open source semblent elles aussi utiliser principalement les API intégrées
Plutôt que d’essayer d’ajouter encore un exécutable en WebAssembly, j’aimerais voir une solution open source qui fournisse une API proche de SQLite et de la synchronisation par-dessus le stockage natif du navigateur
WHEREau lieu d’un filtreif()pourrait aussi consommer moins de mémoire et de CPUMême en le téléchargeant depuis la page officielle et en supprimant tous les fichiers
mjsainsi que les fichiers « bundler-friendly », la dépendance SQLite Wasm minimale pèse environ 1,3 MoCela semble un peu gros pour une application navigateur, mais comme Wasm s’exécute aujourd’hui aussi ailleurs, cela peut avoir davantage de sens dans ces environnements
sqlite3.jset le Wasm font 1,3 Mo bruts, mais en minifiant le JS et en compressant les deux fichiers avec Brotli, on descend à 410 KoS’il ne s’agit que de stocker des préférences utilisateur, bien sûr, il n’est pas nécessaire de télécharger SQLite ; mais si une vraie base de données apporte un bénéfice pour la tâche visée, il n’y a pas lieu de trop s’inquiéter pour 1 Mo et de réinventer inutilement la roue
Si l’autre commentaire est exact, cela ne fait de toute façon même pas 1,3 Mo sur le réseau
Cela dit, pour des applications navigateur dont le code applicatif et les données sont déjà volumineux, comme des apps à la Figma ou Photoshop, ou des apps de machine learning, 1,3 Mo peut ne pas représenter une surcharge importante
Il faut aussi garder à l’esprit que le parsing du Wasm est nettement plus rapide que celui du JS. Je ne retrouve pas de source pour cette affirmation pour l’instant, mais il y avait au moins un bon article à ce sujet
https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
Cela peut être en local, sur un serveur central, ou sur un edge proche
Par ailleurs, WASI est encore loin d’être une réponse. L’amalgamation SQLite se compile bien avec WASI, mais la concurrence reste un problème non résolu
Il a fallu créer un VFS à partir de zéro pour rendre un pilote SQLite basé sur Wasm utilisable
https://github.com/ncruces/go-sqlite3/blob/main/vfs/README.md
J’ai essayé pendant un moment de faire fonctionner ça dans l’écosystème Rust, mais les wrappers SQL de blessed.rs,
rusqliteetsqlx, ne semblaient pas encore pouvoir en tirer partiL’envelopper soi-même était également délicat : je n’ai pas trouvé comment faire en sorte que le code Wasm d’emscripten fonctionne correctement avec
wasm32-unknown-unknownAu final, il faut un wrapper JS sous une forme ou une autre, puis exposer depuis JS l’interface attendue par ces crates. Si cela se résout côté Rust, ce sera vraiment intéressant
En substance, il faut importer le build emscripten de SQLite dans
wasm-bindgenvia des en-têtesextern C, compiler en mode multithread pour permettre l’accès aux buffers de tableaux partagés, et réimplémenter le VFS en RustEnsuite, les lignes SQLite deviennent accessibles sous forme d’octets Wasm bruts, et il devient possible de créer un wrapper ou une intégration de style Rust-SQLite
Il n’y aura toujours pas de fonctionnalités pratiques comme le pooling de connexions, mais en Wasm il est probablement préférable d’utiliser la DB en mode exclusif
Une release 0.2 est en préparation cette semaine, avec quelques problèmes à régler. À part cela, on peut utiliser Diesel ORM comme en natif
emscriptenetwasm32-unknown-unknownIl existe un projet visant à faire mieux fonctionner ensemble Emscripten et Rust sur Wasm : https://github.com/rustwasm/wasm-bindgen/pull/4014#issuecomment-2356908451
J’ai créé une bibliothèque SQLite qui s’exécute sur une JVM pure, en utilisant le build Wasm de SQLite et le runtime Chicory : https://github.com/dylibso/sqlite-zero
C’est plutôt une expérimentation qu’un projet visant la production, mais je pense que ça peut être utile pour apporter un outil SQLite sans dépendances à l’écosystème JVM
Question générale : dans quelles situations est-il plus avantageux d’envoyer toute la DB et de laisser le navigateur traiter les requêtes ?
Autrement dit, quand utiliserait-on cela pour améliorer l’expérience utilisateur par rapport au modèle traditionnel de DB hébergée côté serveur ?
Avec le bon driver, on peut streamer les chunks nécessaires de la base de données au lieu d’envoyer toute la base : https://github.com/mmomtchev/sqlite-wasm-http
On peut faire de la recherche plein texte SQLite sans télécharger toute la base FTS. Si la requête est assez courte, on en récupère la plus grande partie, mais pas la totalité
https://medium.com/offline-camp/couchdb-pouchdb-and-hoodie-as-a-stack-for-progressive-web-apps-a6078a985f18
Cela peut aller d’une bibliothèque audio à un logiciel de modélisation 3D. On peut créer et persister les modifications en local, puis les synchroniser avec le côté serveur périodiquement ou lorsque l’on repasse en ligne
Ce type d’app est un bon candidat pour une DB embarquée côté client