SQLite3-WebAssembly-Dokumentationsindex
(sqlite.org)- Ein Hub, der die WebAssembly- und JavaScript-API-Dokumentation an einem Ort bündelt, für Entwickler, die sqlite3 direkt verwenden möchten in aktuellen Browsern mit WASM-Unterstützung
- Für den schnellen Einstieg beginnt man mit dem 3-Schritte-HOWTO; außerdem lassen sich Installations- und Bereitstellungspfade über Dokumente zu Downloads, npm-Modul, Build und Emscripten-Details nachvollziehen
- Die API-Dokumentation reicht von Ladeverfahren und Client-Code-Rezepten über persistenten Speicher und C-Strukturen in der JS-API bis hin zum Schreiben virtueller Tabellen und Table-Valued Functions
- Reale Einsatzbeispiele wie SQLime, Evolu, SQLiteNext, sqlite-wasm-esm und sqlite-wasm-http werden zusammen mit früheren Projekten wie sql.js, wa-sqlite und absurd-js aufgeführt
- Verknüpft Materialien zu Browser-SQLite, OPFS, IndexedDB und WASI, sodass sich Implementierungsoptionen für den Einsatz von SQLite im Web schnell vergleichen lassen
Umfang der sqlite3-WASM-Dokumentation
- Dokumentation der WebAssembly- und JavaScript-API des SQLite-Projekts; behandelt die APIs, mit denen sich sqlite3 in aktuellen Browsern mit WASM-Unterstützung verwenden lässt
-
Projektinformationen und Support-Kanäle
- About the sqlite3 WASM subproject
- Project news
- FAQ: Frequently Asked Questions
- TODOs: Behandelt sowohl geplante als auch nicht geplante Funktionen
- Technischen Support und Feedback gibt es im SQLite forum; Inhaber von commercial SQLite support contracts können ihre bestehenden Support-Kanäle nutzen
-
Ausführungs-, Installations- und Build-Dokumentation
- three-step HOWTO: Zeigt, wie das sqlite3-WASM-Modul und die zugehörige JavaScript-API eingebunden und ausgeführt werden
- Downloads: Verfügbar auf der Download-Seite des Hauptprojekts
- Prerelease snapshots: Werden fortlaufend aktualisiert
- npm module
- Building sqlite3 WASM: Behandelt das Build-Verfahren für sqlite3 WASM und den zugehörigen JS-Code
- Build-Details zu Emscripten
-
API- und Implementierungsdetails
- The API index: Erklärt verschiedene API-Varianten sowie Lade- und Zugriffsmethoden
- Cookbook: Rezepte für Code auf Client-Ebene
- Persistent storage options: Optionen für persistenten Speicher
- C-structs: Wie C-Strukturen in der JS-API verwendet werden
- virtual tables: Wie sich virtuelle Tabellen und Table-Valued Functions in JS erstellen lassen
- API changes: API-Änderungen, die sich auf Clients auswirken können
- module symbols app: Erzeugt eine Liste der sqlite3-API-Symbole im JavaScript-/WASM-Modul
-
Dokumentations- und Repository-Pflege
Anwendungsfälle und angrenzendes Ökosystem
-
Drittprojekte, die dieses Projekt nutzen
- SQLime: Bietet eine Datenbank-Browser-Oberfläche
- Evolu: Eine Local-first-Plattform mit Fokus auf Privatsphäre, Benutzerfreundlichkeit und fehlenden Vendor Lock-in
- SQLiteNext: Demo zur Integration dieses Projekts mit next.js
- sqlite-wasm-esm: Zeigt, wie dieses Projekt im Vite-Toolchain eingesetzt wird
- sqlite-wasm-http: SQLite-VFS, das schreibgeschützten Zugriff auf direkt per HTTP bereitgestellte Datenbanken bietet
-
Vorarbeiten und benachbarte Projekte
- Alon Zakais sql.js: Gilt, abgesehen von WebSQL, als der erste Fall direkter sqlite3-Nutzung im Browser und lässt sich bis zu einem Commit von 2012 zurückverfolgen
- Roy Hashimotos wa-sqlite: Gilt als erste Implementierung von OPFS storage für sqlite3-Datenbanken und umfasst auch Arbeiten rund um SQLite VFS auf der JS-Seite
- James Longs absurd-js: Zeigt, wie sich sqlite3-Datenbanken in einer IndexedDB-Datenbank speichern lassen
- postgres-wasm: Führt einen Postgres database server im Browser aus
- Jaccwabyt: Eine kleine JS-Bibliothek zur Manipulation von in WASM gehosteten C-Strukturen per JS-Code; wurde zur Unterstützung der OPFS sqlite3_vfs-Implementierung dieses Projekts erstellt
- CoWasm: „Collaborative WebAssembly for Servers and Browsers“ und enthält in der Demo-App einen WASM-Build der sqlite3-shell-Anwendung
- absurder-js: Nicholas G. Piescos Neuinterpretation von absurd-js; dazu gibt es auch eine Vorstellung im SQLite forum
- sqlite-wasm-rs: Bietet Rust-Bindings für SQLite für
wasm32-unknown-unknown-Builds
-
Externe Dokumentation und Artikel
- Porting WebSQL to OPFS: Artikel des Google-Chrome-Dev-Teams zu SQLite WASM
- building SQLite3 for WASI: Artikel des VMWare-OCTO-Teams zum SQLite3-Build für WASI
1 Kommentare
Hacker-News-Kommentare
Es wäre interessant, In-Memory-SQLite im Browser laufen zu lassen und Kopien des WAL-Logs auf den Server zu streamen, ähnlich wie es Litestream und Cloudflare Durable Objects machen.
Man könnte WebSocket verwenden, oder es scheint auch mit gelegentlichen
fetch()-POSTs möglich. Bei späteren Besuchen würde dann die clientseitige SQLite-Datenbank anhand der serverseitigen Daten wieder befüllt.Allerdings fehlt laut diesem SQLite-Forum-Beitrag im Standard-SQLite-WASM-Build der WAL-Modus, daher wäre wohl ein Custom-Build nötig.
In-Memory-SQLite verwendet kein WAL, und Browser-Wasm unterstützt Dinge wie die Shared-Memory-API nicht, die SQLite für seinen WAL-Modus erwartet.
Litestream benötigt eine sehr genaue WAL-Konfiguration, die bei der nativen SQLite-Standardkonfiguration zufällig gut passt, sich in Wasm aber schwer nachbilden lässt.
Cloudflare Durable Objects mögen von Litestream inspiriert sein, funktionieren tatsächlich aber ziemlich anders; dasselbe gilt für LiteFS oder Turso.
Die Grundidee, SQLite-Änderungen zu streamen, ist an sich möglich, erfordert aber viel Arbeit und ist wegen des Nebenläufigkeitsmodells von Browser-Wasm schwierig umzusetzen.
Ich habe den betreffenden Forumsbeitrag damals geschrieben und WAL in serverseitigen Wasm-SQLite-Builds zum Laufen gebracht, aber für den Browser war das keine praktikable oder brauchbare Option.
justjs14.Den Code von damals müsste ich erst suchen, aber er kompilierte SQLite mit VFS für den Browser nach Wasm, öffnete eine DB, nahm Änderungen vor und konnte das WAL zum Server pushen bzw. von dort pullen. Synchronisierung zwischen Browsern war ebenfalls möglich, manuell oder per WebRTC; wenn man dem Browser-Client ein GitHub-Token gab, lief es sogar auf GitHub Pages.
Bei Interesse könnte ich es noch einmal von Grund auf lauffähig machen. Ich habe damals viel mit diesem Ansatz experimentiert und halte ihn zumindest für manche Anwendungen für nützlich.
Eine Demo nur mit Pull-Funktion gibt es hier: https://just.billywhizz.io/sqlite/demo/#https://just.billywhizz.io/sqlite/demo/db/chinook.db
„Shared Memory“ könnte
SharedArrayBuffersein, und wenn man jeweils nur eine DB-Verbindung öffnet, ginge auch ein normaler Buffer. Zum Beispiel kann man das in einem ohnehin bereits verbreitetenSharedWorkerso machen.Die Implementierung wäre nicht trivial, aber definitiv möglich. Das größere praktische Problem ist, dass man Änderungen auf Blockebene erfasst, nicht auf Zeilenebene.
Daher eignet es sich für die Replikation der gesamten DB, aber teilweise Synchronisierung, etwa nur einzelne Zeilen mit anderen Nutzern zu teilen, ist schwierig.
Wenn man für Teilsynchronisierung Änderungen auf Zeilenebene braucht, sollte man Trigger oder die SQLite-Session-Extension verwenden: https://sqlite.org/sessionintro.html
Bei PowerSync verwenden wir clientseitige Trigger und haben außer dem Wartungsaufwand für die Trigger keine großen Nachteile gefunden.
Wenn man zum Beispiel allgemeines Template-Rendering oder einige Htmx-Endpunkte in Wasm laufen lässt, könnten auch normale serverseitige Web-Frameworks mit einem SQLite-Wasm-Speicher, wie ihn OP beschreibt, Offline-Funktionen bereitstellen.
Vollständiges Django-Beispiel: https://github.com/m-butterfield/django_webassembly
Minimales Rust-Beispiel mit Makros zur Trennung von Client/Server: https://github.com/richardanaya/wasm-service
Clojure Electric macht Ähnliches ohne Wasm, dafür mit mehr Makros: https://github.com/hyperfiddle/electric
Außerdem gibt es unter den neueren Web-Tools von Microsoft eines, das mit B anfängt.
In den letzten Jahren hat ein überraschend großer Teil des Python-Ökosystems Wasm-Unterstützung bekommen und ist dadurch für diesen Ansatz nutzbar geworden. Nicht nur offensichtliche Machine-Learning-Bibliotheken, sondern auch Spielebibliotheken wie Pygame und Pyxel; und da SDL2 Unterstützung hinzugefügt hat, scheint auch Support für die Multitouch-UI-Bibliothek Kivy bald möglich.
Es eröffnen sich viele neue Möglichkeiten, Code auf beliebigen Computern auszuführen und bei Bedarf innerhalb der Sandbox eines Webbrowsers Plattformbürokratie und Gebühren zu umgehen.
In der Dokumentation steht, dass diese Komponenten erstmals in 3.40 als öffentliche Beta erschienen sind und dass die API in 3.41 anhand von Community-Feedback stabilisiert werden sollte.
Da das neueste SQLite-Release aber 3.46.1 vom 13.08.2024 ist, ist die API vermutlich bereits stabilisiert, nur die Dokumentation wurde wohl nicht aktualisiert.
Es wäre ein guter Distributionsweg, wenn das SQLite-Team ein offizielles npm-Paket mit der WASM-Version veröffentlichen würde. Später habe ich gesehen: Laut Antwort gibt es das schon.
Meine persönliche Lieblingsvariante von SQLite-in-WASM ist weiterhin die Pyodide-Variante. Sie gibt es schon viel länger als die offizielle SQLite-Implementierung, und wenn man Pyodide verwendet, kommt WASM-SQLite als Teil der Python-Standardbibliothek mit.
So wird es auf https://lite.datasette.io/ verwendet, und man kann es auch unter https://pyodide.org/en/stable/console.html ausprobieren.
Wenn man mit
sqlite3.connect(':memory:')einselect sqlite_version()ausführt, erhält man 3.39.0 vom 25.06.2022; Pyodide scheint also ein Versionsupdate zu benötigen. Diese Version wird offenbar von emscripten geerbt: https://github.com/emscripten-core/emscripten/blob/main/tools/ports/sqlite3.pyAllerdings wird das SQLite-Team kein offizielles npm-Paket veröffentlichen. Wir liefern nur reines JS aus und weigern uns entschieden, uns in das Kaninchenloch der Tool-Unterstützung außerhalb der Sprache zu begeben. Keines der Projektmitglieder nutzt solche Tools.
Wir unterstützen zwar einen „offiziell genehmigten“ npm-Build, den Thomas Steiner pflegt, aber wir nehmen kein JS-Framework aktiv als Entwicklungsziel ins Visier.
Wenn wir ein bestimmtes Framework, einschließlich npm, direkt unterstützen, könnte der Eindruck entstehen, wir würden dieses Framework empfehlen, und wir vermeiden aktiv Empfehlungen für Drittanbieterprojekte.
@sqlite.org/sqlite-wasmüberjsdelivrimportieren undsqlite3-bundler-friendly.mjssowiesqlite3.wasmladen.Dann kann man die C-API von SQLite und die objektorientierte API
oo1nutzen, und es funktioniert sowohl mit normalentype=module-Script-Tags als auch in Deno.Beispiel-HTML gibt es hier: https://github.com/Ciantic/experimenting-sqlite-wasm/blob/main/standalone.html
sqlite-wasmlädt deutlich schneller als Pyodide; wenn man also kein Python braucht, ist Ersteres die bessere Wahl.Es scheint allerdings ziemlich eingeschränkt zu sein. Da es kein
sqlite_version()gibt, konnte ich nicht einmal die Version prüfen, und die Version im Repository ist mit 3.38.1 ziemlich alt.Die aktuelle DuckDB-Web-Shell kann die SQLite-Erweiterung nicht laden, weil die SQLite-Erweiterung für die gestern erschienene Version 1.1.2 noch nicht veröffentlicht wurde. Die ältere Version funktioniert mit der kürzlich aktualisierten WASM-Edition.
Man kann auch Spatial-Erweiterungen, GDAL, Vektorsuche usw. anbinden: https://github.com/duckdb/duckdb-wasm/releases/tag/v1.29.0
Eine eigene „SQL-Web-Shell“ zu bauen war ebenfalls nicht allzu schwer, aber die Dokumentation war nicht vollständig genug.
https://github.com/duckdb/sqlite_scanner/blob/main/src/sqlite/sqlite3.h
Für die Nutzung mit Go gefällt mir ncruces’ Wasm-SQLite-Paket https://github.com/ncruces/go-sqlite3 wirklich gut.
Auch das Go-Paket von cznic ist hervorragend, aber diese Wasm-Version funktioniert auch in Umgebungen wie OpenBSD gut.
WebSQL hätte einfach SQLite sein sollen; dann wäre das Ökosystem für Offline-first und App-Speicher insgesamt viel besser gewesen.
Gibt es jetzt noch eine Chance? Statt SQLite zu abstrahieren und übermäßig zu spezifizieren: Könnte man nicht die Version der SQLite-API spezifizieren, die Browser unterstützen müssen, und diese Version regelmäßig anheben?
Ein Kernprinzip des Webs ist, dass Bestehendes nach Möglichkeit weiter funktionieren soll, und solche Breaking Changes sind schwierig.
Selbst wenn die Spezifikation mehrere Versionen auflistet und Webseiten auswählen lässt, müssten Browser die heikle Last tragen, alte SQLite-Versionen vorzuhalten, die möglicherweise gar nicht mehr unterstützt werden. Jede Version belegt zudem etwa 1 MB.
Dann scheint es mir besser, einfach wie jetzt jede Website die Kosten ihrer eigenen Entscheidung tragen zu lassen.
Wenn es darum geht, das wiederholte Herunterladen gemeinsamer Ressourcen zu vermeiden: Das ist ein Problem, das auch andere CDNs bereits akzeptiert haben. Eine Lösung für Shared Caching gemeinsamer Abhängigkeiten wäre deutlich wertvoller, als sie nur auf SQLite zu beschränken.
Der aktuelle Ansatz wirkt besser als eine vom Browser bereitgestellte Version.
Heute kann man bei Bedarf das aktuelle SQLite als 410 KB großes komprimiertes WASM-Blob laden. Wären wir an die vom Browser erzwungene SQLite-Version gebunden, würden wir inzwischen vielleicht eine zehn Jahre alte Version verwenden.
Beim schnellen Überfliegen der Seite ist mir der Anwendungsfall nicht ganz klar.
Konkreter: Kann das ein Ersatz für IndexedDB sein? Werden die Daten persistent gespeichert, oder muss man die SQLite-Datei selbst über die File System API bzw. in IndexedDB/localStorage ablegen?
Derzeit umfasst das Speicherung über das Origin-Private FileSystem (OPFS) sowie, wenn auch stark eingeschränkt, Backends für
window.localStorageundwindow.sessionStorage.Ich interessiere mich in letzter Zeit stark für den Local-first-Bereich, aber verglichen mit den eingebauten Storage-APIs des Browsers, insbesondere IndexedDB, wirkt das Einbetten von SQLite ziemlich schwergewichtig.
Auch die wichtigsten Open-Source-Bibliotheken scheinen meist die eingebauten APIs zu verwenden.
Statt per WebAssembly noch eine weitere ausführbare Datei einzubinden, würde ich gern eine Open-Source-Lösung sehen, die auf dem nativen Browser-Storage eine SQLite-ähnliche API und Synchronisierung bereitstellt.
if()-Filtern eineWHERE-Klausel verwendet, könnte das Speicher und CPU sparen.Selbst wenn man es von der offiziellen Download-Seite lädt und alle
mjs-Dateien sowie die „bundler-friendly“-Dateien entfernt, liegt die minimale SQLite-Wasm-Abhängigkeit bei etwa 1,3 MB.Für Browser-Apps wirkt das eher groß, aber da Wasm heutzutage auch an anderen Orten läuft, kann es in solchen Umgebungen sinnvoller sein.
sqlite3.jsund Wasm sind im Original 1,3 MB groß, aber wenn man das JS minimiert und beide Dateien mit Brotli komprimiert, kommt man auf 410 KB.Wenn es nur um das Speichern von Nutzereinstellungen geht, muss man SQLite natürlich nicht herunterladen; aber wenn eine vollständige Datenbank für eine Aufgabe Vorteile bringt, sollte man wegen 1 MB nicht zu sehr besorgt sein und unnötig das Rad neu erfinden.
Wenn der andere Kommentar stimmt, sind es übers Netzwerk ohnehin nicht einmal 1,3 MB.
Dennoch kann bei Browser-Apps, deren Anwendungscode und Daten ohnehin groß sind, etwa Figma- oder Photoshop-ähnlichen Apps oder Machine-Learning-Apps, 1,3 MB kaum zusätzliche Last bedeuten.
Außerdem sollte man bedenken, dass Wasm deutlich schneller geparst wird als JS. Ich finde gerade keine Quelle für diese Behauptung, aber es gab mindestens einen guten Artikel dazu.
https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
Das kann lokal sein, auf einem zentralen Server oder an einer nahegelegenen Edge.
Außerdem ist WASI noch weit davon entfernt, die Lösung zu sein. Die SQLite-Amalgamation lässt sich zwar gut mit WASI bauen, aber Nebenläufigkeit ist ein ungelöstes Problem.
Um einen Wasm-basierten SQLite-Treiber brauchbar zu machen, musste ich ein VFS von Grund auf erstellen.
https://github.com/ncruces/go-sqlite3/blob/main/vfs/README.md
Ich habe eine Weile versucht, das im Rust-Ökosystem zum Laufen zu bringen, aber die SQL-Wrapper von blessed.rs,
rusqliteundsqlx, schienen das noch nicht nutzen zu können.Es war auch schwierig, selbst einen Wrapper zu schreiben, weil ich keinen Weg gefunden habe, Emscripten-Wasm-Code sauber mit
wasm32-unknown-unknownzusammenarbeiten zu lassen.Am Ende braucht man in irgendeiner Form einen JS-Wrapper, und dann muss man die von diesen Crates erwarteten Interfaces in JS implementieren und bereitstellen. Wenn das in Rust gelöst wird, wäre das wirklich großartig.
Im Kern muss man in
wasm-bindgeneinen SQLite-Emscripten-Build überextern C-Header einbinden, mit Mehrthreadmodus kompilieren, damit Zugriff auf Shared Array Buffers möglich ist, und das VFS in Rust neu implementieren.Danach kann man auf SQLite-Zeilen als rohe Wasm-Bytes zugreifen und einen Rust-SQLite-artigen Wrapper oder eine Integration bauen.
Komfortfunktionen wie Connection Pooling wird es weiterhin nicht geben, aber in Wasm ist es wahrscheinlich besser, die DB im Exklusivmodus zu verwenden.
Diese Woche bereite ich das Release 0.2 vor und werde einige Probleme bereinigen. Abgesehen davon kann man das Diesel ORM wie nativ verwenden.
emscripten-Wasm-Code undwasm32-unknown-unknowngibt es gute Nachrichten.Es gibt einen Plan, Emscripten und Rust in Wasm gut zusammenarbeiten zu lassen: https://github.com/rustwasm/wasm-bindgen/pull/4014#issuecomment-2356908451
Ich habe mit dem Wasm-Build von SQLite und der Chicory-Runtime eine SQLite-Bibliothek gebaut, die auf einer reinen JVM läuft: https://github.com/dylibso/sqlite-zero
Das ist eher ein Experiment als auf Produktionsreife ausgelegt, könnte aber nützlich sein, um ein abhängigkeitsfreies SQLite-Tool ins JVM-Ökosystem zu bringen.
Allgemeine Frage: In welchen Situationen ist es vorteilhafter, die gesamte DB auszuliefern und den Browser die Queries verarbeiten zu lassen?
Anders gesagt: Wann würde man das einsetzen, um die User Experience gegenüber dem traditionellen Modell einer servergehosteten DB zu verbessern?
Mit dem passenden Treiber kann man statt der gesamten Datenbank nur die benötigten Datenbank-Chunks streamen: https://github.com/mmomtchev/sqlite-wasm-http
So kann man SQLite-Volltextsuche nutzen, ohne die gesamte FTS-Datenbank herunterzuladen. Wenn der Suchbegriff kurz genug ist, bekommt man zwar den Großteil, aber nicht alles.
https://medium.com/offline-camp/couchdb-pouchdb-and-hoodie-as-a-stack-for-progressive-web-apps-a6078a985f18
Das reicht von Audiobibliotheken bis zu 3D-Modellierungssoftware. Änderungen lassen sich lokal erstellen und persistieren und dann regelmäßig oder sobald man wieder online ist mit der Serverseite synchronisieren.
Solche Apps sind gute Kandidaten für eine clientseitig eingebettete DB.