Índice da documentação do SQLite3 WebAssembly
(sqlite.org)- Um hub que reúne em um só lugar a documentação de WebAssembly e da API JavaScript para desenvolvedores que querem usar o sqlite3 diretamente em navegadores modernos com suporte a WASM
- Para uma execução rápida, comece pelo HOWTO em 3 etapas; também é possível conferir os caminhos de instalação e distribuição na documentação de downloads, módulo npm, build e detalhes do Emscripten
- A documentação da API abrange formas de carregamento, receitas de código cliente, armazenamento persistente, estruturas C na API JS, até a criação de tabelas virtuais e funções com valor de tabela
- Organiza casos de uso reais como SQLime, Evolu, SQLiteNext, sqlite-wasm-esm e sqlite-wasm-http, junto com projetos predecessores como sql.js, wa-sqlite e absurd-js
- Conecta materiais sobre SQLite no navegador, OPFS, IndexedDB e WASI, permitindo comparar rapidamente opções de implementação para usar SQLite no ambiente web
Escopo da documentação do sqlite3 WASM
- É a documentação de WebAssembly e da API JavaScript do projeto SQLite, cobrindo APIs que permitem usar o sqlite3 em navegadores modernos com suporte a WASM
-
Orientações do projeto e canais de suporte
- About the sqlite3 WASM subproject
- Project news
- FAQ: Frequently Asked Questions
- TODOs: cobre tanto recursos planejados quanto não planejados
- Suporte técnico e feedback são recebidos no SQLite forum, e quem possui commercial SQLite support contracts pode usar os canais de suporte existentes
-
Documentação de execução, instalação e build
- three-step HOWTO: demonstra como incluir e executar o módulo sqlite3 WASM e a API JavaScript relacionada
- Downloads: fornecidos na página de downloads do projeto principal
- Prerelease snapshots: atualizados com frequência
- npm module
- Building sqlite3 WASM: cobre o procedimento de build do sqlite3 WASM e do código JS relacionado
- Detalhes de build com Emscripten
-
API e detalhes de implementação
- The API index: explica várias variações de API e formas de carregamento e acesso
- Cookbook: receitas para código em nível de cliente
- Persistent storage options: opções de armazenamento persistente
- C-structs: como usar estruturas C na API JS
- virtual tables: como criar tabelas virtuais e funções com valor de tabela em JS
- API changes: mudanças de API que podem afetar clientes
- module symbols app: gera uma lista de símbolos da API sqlite3 no módulo JavaScript/WASM
-
Manutenção da documentação e do repositório
Casos de uso e ecossistema relacionado
-
Projetos de terceiros que usam este projeto
- SQLime: oferece uma interface de navegador de banco de dados
- Evolu: plataforma local-first focada em privacidade, facilidade de uso e ausência de dependência de fornecedor
- SQLiteNext: demo que integra este projeto ao next.js
- sqlite-wasm-esm: demonstra como usar este projeto no toolchain Vite
- sqlite-wasm-http: SQLite VFS que fornece acesso somente leitura a bancos de dados servidos diretamente via HTTP
-
Trabalhos anteriores e projetos adjacentes
- sql.js, de Alon Zakai: é conhecido como o primeiro caso de uso direto do sqlite3 no navegador, exceto pelo WebSQL, e remonta a um commit de 2012
- wa-sqlite, de Roy Hashimoto: conhecido como a primeira implementação de armazenamento OPFS para bancos de dados sqlite3, também incluindo trabalhos relacionados a SQLite VFS no lado JS
- absurd-js, de James Long: demonstra uma forma de armazenar bancos de dados sqlite3 dentro de um banco de dados IndexedDB
- postgres-wasm: executa um servidor de banco de dados Postgres no navegador
- Jaccwabyt: pequena biblioteca JS para manipular, a partir de código JS, estruturas C hospedadas em WASM; foi criada para apoiar a implementação de OPFS sqlite3_vfs deste projeto
- CoWasm: “Collaborative WebAssembly for Servers and Browsers”, e inclui um build WASM da aplicação sqlite3 shell no demo app
- absurder-js: projeto de Nicholas G. Piesco que reimagina o absurd-js, também com um anúncio no SQLite forum
- sqlite-wasm-rs: fornece bindings Rust para SQLite voltados ao build
wasm32-unknown-unknown
-
Documentação externa e artigos
- Porting WebSQL to OPFS: artigo da equipe de desenvolvimento do Google Chrome sobre SQLite WASM
- building SQLite3 for WASI: artigo da equipe VMWare OCTO sobre build do SQLite3 para WASI
1 comentários
Opiniões do Hacker News
Seria interessante rodar um SQLite em memória no navegador e fazer streaming de cópias do log WAL para o servidor, como no modo usado pelo Litestream e pelos Cloudflare Durable Objects
Poderia ser com WebSocket, ou aparentemente também com POSTs
fetch()intermitentes. Em visitas posteriores, os dados do lado do servidor seriam usados para repopular o SQLite do clientePorém, olhando esta publicação no fórum do SQLite, parece que a build padrão do SQLite WASM não inclui o modo WAL, então talvez seja necessária uma build customizada
SQLite em memória não usa WAL, e o Wasm no navegador não oferece coisas como a API de memória compartilhada que o modo WAL do SQLite espera
O Litestream exige uma configuração de WAL muito precisa; na configuração padrão do SQLite nativo isso acaba coincidindo bem, mas é difícil reproduzir em Wasm
Os Cloudflare Durable Objects podem até ter sido inspirados pelo Litestream, mas o funcionamento real é bastante diferente, e o mesmo vale para LiteFS ou Turso
A ideia geral de fazer streaming das alterações do SQLite em si é possível, mas dá bastante trabalho, e o modelo de concorrência do Wasm no navegador torna a implementação complicada
Escrevi aquela publicação no fórum algum tempo atrás e consegui fazer WAL funcionar em builds de SQLite Wasm do lado do servidor, mas não era uma opção utilizável ou viável no navegador
justjs14Eu teria que procurar o código da época, mas compilei para Wasm um SQLite com VFS no navegador, abria o DB, fazia alterações e permitia enviar/receber o WAL do servidor. Também era possível sincronizar entre navegadores, manualmente ou via WebRTC, e funcionava até no GitHub Pages se você desse um token do GitHub ao cliente no navegador
Se houver interesse, posso tentar refazer do zero em uma forma executável. Na época experimentei bastante com essa abordagem e acho que ela pode ser útil pelo menos para algumas aplicações
Há também uma demo apenas com funcionalidade de pull aqui: https://just.billywhizz.io/sqlite/demo/#https://just.billywhizz.io/sqlite/demo/db/chinook.db
A “memória compartilhada” pode ser
SharedArrayBuffer, e se você abrir apenas uma conexão de DB por vez, um buffer comum também serve. Por exemplo, isso pode ser feito dentro de umSharedWorker, que já é algo comumA implementação não seria simples, mas é claramente possível. Na prática, o problema maior é que você captura alterações em nível de bloco, não em nível de linha
Por isso serve para replicação do DB inteiro, mas é difícil fazer sincronização parcial, como compartilhar algumas linhas com outros usuários
Se você precisa de alterações em nível de linha para sincronização parcial, deve usar triggers ou algo como a extensão de sessão do SQLite: https://sqlite.org/sessionintro.html
No PowerSync usamos triggers do lado do cliente e, fora o trabalho de manutenção dos triggers, não encontrei grandes desvantagens
Por exemplo, se a renderização de templates comuns ou alguns endpoints Htmx rodassem em Wasm, mesmo frameworks web comuns do lado do servidor poderiam oferecer funcionalidade offline com um armazenamento SQLite Wasm parecido com o que o OP mencionou
Exemplo completo em Django: https://github.com/m-butterfield/django_webassembly
Exemplo mínimo em Rust com macros separando cliente/servidor: https://github.com/richardanaya/wasm-service
Clojure Electric, que faz algo parecido sem Wasm e com mais macros: https://github.com/hyperfiddle/electric
Há também uma ferramenta web recente da Microsoft que começa com B
Nos últimos anos, uma quantidade surpreendente do ecossistema Python ganhou suporte a Wasm e passou a poder ser usada nessa abordagem. Não só bibliotecas obviamente relacionadas a machine learning, mas também bibliotecas de jogos como Pygame e Pyxel; como o SDL2 adicionou suporte, o suporte ao Kivy, uma biblioteca de UI multitoque, também parece estar próximo
Parece haver muitas novas possibilidades de executar código em qualquer computador e, quando necessário, dentro do sandbox do navegador, evitando burocracias e taxas de plataformas
A documentação diz que esses componentes apareceram pela primeira vez na versão 3.40 como beta público e que a API seria estabilizada na 3.41 com base no feedback da comunidade.
Mas a versão mais recente do SQLite é a 3.46.1, de 13/08/2024, então provavelmente a API já foi estabilizada, mas a documentação não foi atualizada.
Acho que seria um bom meio de distribuição se a equipe do SQLite lançasse um pacote npm oficial com a versão WASM. Depois vi que, segundo uma resposta, isso já existe.
Pessoalmente, meu SQLite-in-WASM favorito ainda é a variante do Pyodide. Ela existe há muito mais tempo que a implementação oficial do SQLite, e, ao usar Pyodide, o SQLite em WASM vem junto como parte da biblioteca padrão do Python.
É assim que ele é usado em https://lite.datasette.io/, e também dá para testar em https://pyodide.org/en/stable/console.html.
Ao executar
select sqlite_version()comsqlite3.connect(':memory:'), aparece a versão 3.39.0, de 25/06/2022, então parece que o Pyodide precisa de uma atualização de versão. Essa versão parece ser herdada do emscripten: https://github.com/emscripten-core/emscripten/blob/main/tools/ports/sqlite3.pyNo entanto, a equipe do SQLite não vai lançar um pacote npm oficial. Nós distribuímos apenas JS puro e nos recusamos firmemente a entrar na toca do coelho do suporte a ferramentas fora da linguagem. Nenhum membro do projeto usa essas ferramentas.
Damos suporte ao build npm “oficialmente aprovado” mantido por Thomas Steiner, mas não miramos ativamente nenhum framework JS para desenvolvimento.
Dar suporte direto a um framework específico, incluindo npm, pode passar a impressão de endossar esse framework, e nós evitamos ativamente endossar projetos de terceiros.
@sqlite.org/sqlite-wasmviajsdelivre carregarsqlite3-bundler-friendly.mjsesqlite3.wasm.Com isso, é possível usar a API C do SQLite e a API orientada a objetos
oo1, e funciona tanto com tags de script comunstype=modulequanto no Deno.Há um exemplo de HTML aqui: https://github.com/Ciantic/experimenting-sqlite-wasm/blob/main/standalone.html
O
sqlite-wasmcarrega muito mais rápido que o Pyodide, então, se você não precisa de Python, o primeiro é uma opção melhor.Porém, parece ser bastante limitado. Como não há
sqlite_version(), nem consegui verificar a versão, e a versão no repositório é a 3.38.1, bem antiga.Atualmente, o shell web do DuckDB não consegue carregar a extensão do SQLite porque a extensão do SQLite para a 1.1.2, lançada ontem, ainda não foi publicada. A versão anterior funciona com a edição WASM atualizada recentemente.
Também é possível acoplar extensão espacial, GDAL, busca vetorial etc.: https://github.com/duckdb/duckdb-wasm/releases/tag/v1.29.0
Criar seu próprio “shell SQL web” também não foi muito difícil, mas a documentação não era completa o suficiente.
https://github.com/duckdb/sqlite_scanner/blob/main/src/sqlite/sqlite3.h
Ao usar em Go, gosto muito do pacote Wasm SQLite do ncruces: https://github.com/ncruces/go-sqlite3
O pacote Go do cznic também é excelente, mas esta versão Wasm funciona bem até em ambientes como OpenBSD.
WebSQL simplesmente deveria ter sido SQLite, e, se tivesse sido, o ecossistema de apps offline-first e de armazenamento de apps em geral seria muito melhor.
Será que ainda há possibilidade agora? Em vez de abstrair o SQLite e especificar demais, não daria para especificar a versão da API do SQLite que os navegadores devem oferecer e atualizar essa versão periodicamente?
Um dos pontos centrais da web é que, sempre que possível, o que já existe deve continuar funcionando, e esse tipo de mudança incompatível é difícil.
Mesmo que a especificação liste algumas versões e permita que a página escolha, os navegadores assumiriam o fardo complicado de manter versões antigas do SQLite que talvez nem sejam mais suportadas. Cada versão também ocupa cerca de 1 MB.
Nesse caso, acho melhor simplesmente deixar cada site arcar com as consequências da sua própria escolha, como acontece hoje.
Se a preocupação é baixar repetidamente recursos comuns, esse é um problema que outras CDNs já aceitaram. Uma solução de cache compartilhado para dependências comuns teria muito mais valor do que algo limitado ao SQLite.
A abordagem atual parece melhor do que uma versão fornecida pelo navegador.
Agora, quando necessário, dá para trazer o SQLite mais recente como um blob WASM comprimido de 410 KB. Se estivéssemos presos à versão do SQLite imposta pelo navegador, talvez hoje estivéssemos usando uma versão de 10 anos atrás.
Passando rapidamente pelas páginas, não fica claro para mim quais são os casos de uso
Mais especificamente: isso pode ser um substituto do IndexedDB? Os dados são persistidos, ou é preciso armazenar diretamente o arquivo SQLite na File System API ou no IndexedDB/localStorage?
Atualmente isso inclui armazenamento via Origin-Private FileSystem (OPFS) e, de forma bem limitada, backends
window.localStorageewindow.sessionStorageUltimamente tenho me interessado bastante pela área de local-first, mas, comparado a usar as APIs de armazenamento embutidas do navegador, especialmente IndexedDB, embutir SQLite parece bem pesado
As principais bibliotecas open source também parecem, em geral, usar as APIs nativas
Em vez de tentar incluir mais um executável via WebAssembly, eu gostaria de ver uma solução open source que oferecesse uma API parecida com SQLite e sincronização em cima do armazenamento nativo do navegador
WHEREem vez de um filtroif()também pode consumir menos memória e CPUMesmo baixando pela página oficial de downloads e removendo todos os arquivos
mjse os arquivos “bundler-friendly”, a dependência mínima do SQLite Wasm fica em cerca de 1,3 MBPara um app de navegador isso parece um pouco grande, mas hoje em dia Wasm também roda em outros lugares, então nesses ambientes pode fazer mais sentido
sqlite3.jse o Wasm têm 1,3 MB no original, mas, minificando o JS e comprimindo os dois arquivos com Brotli, dá para chegar a 410 KBSe for só para salvar preferências do usuário, claro que não faz sentido baixar SQLite; mas, se a tarefa se beneficia de um banco de dados completo, não é preciso se preocupar demais com 1 MB e acabar reinventando a roda à toa
Se o outro comentário estiver certo, na rede nem seriam 1,3 MB para começo de conversa
Ainda assim, em apps de navegador como Figma ou algo parecido com Photoshop, ou apps de machine learning, nos quais o código da aplicação e os dados já são grandes por natureza, 1,3 MB talvez não seja uma sobrecarga adicional significativa
Também vale considerar que o parsing de Wasm é consideravelmente mais rápido que o de JS. Não consegui encontrar agora a fonte dessa afirmação, mas havia pelo menos um bom texto relacionado
https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
Pode ser local, pode ser em um servidor central, ou pode ser na borda próxima
Além disso, WASI ainda está longe de ser uma resposta. A amalgamation do SQLite compila bem para WASI, mas a concorrência continua sendo um problema não resolvido
Foi preciso criar uma VFS do zero para deixar um driver SQLite baseado em Wasm em condição utilizável
https://github.com/ncruces/go-sqlite3/blob/main/vfs/README.md
Por um tempo tentei fazer isso funcionar no ecossistema Rust, mas os wrappers SQL do blessed.rs,
rusqliteesqlx, ainda não pareciam conseguir aproveitar issoTambém era difícil encapsular diretamente, porque não encontrei uma forma de fazer o código Wasm do emscripten funcionar bem com
wasm32-unknown-unknownNo fim, é necessário algum tipo de wrapper JS, e então é preciso implementar a interface esperada por esses crates e expô-la em JS. Se isso for resolvido em Rust, vai ficar muito bom
Essencialmente, é preciso trazer uma build emscripten do SQLite para o
wasm-bindgenvia cabeçalhosextern C, compilar em modo multithread para permitir acesso a shared array buffers e reimplementar a VFS em RustDepois disso, será possível acessar linhas do SQLite como bytes Wasm brutos e criar um wrapper ou integração no estilo Rust-SQLite
Ainda não haveria conveniências como connection pooling, mas em Wasm é provável que seja melhor usar o DB em modo exclusivo
Estamos preparando a versão 0.2 para esta semana e vamos aparar algumas arestas. Fora isso, dá para usar o Diesel ORM como se fosse nativo
emscriptense encaixar bem comwasm32-unknown-unknown, há boas notíciasExiste um plano para fazer Emscripten e Rust funcionarem bem juntos em Wasm: https://github.com/rustwasm/wasm-bindgen/pull/4014#issuecomment-2356908451
Criei uma biblioteca SQLite que roda em JVM pura usando o build Wasm do SQLite e o runtime Chicory: https://github.com/dylibso/sqlite-zero
É mais um experimento do que algo voltado a estar pronto para produção, mas acho que pode ser útil para trazer uma ferramenta SQLite sem dependências para o ecossistema JVM
Uma pergunta geral: em que situações é mais vantajoso enviar o DB inteiro e deixar o navegador processar as queries?
Em outras palavras, quando você usaria isso para melhorar a experiência do usuário em comparação com o modelo tradicional de DB hospedado no servidor?
Com o driver adequado, dá para transmitir em streaming os chunks necessários do banco de dados em vez de enviar o banco inteiro: https://github.com/mmomtchev/sqlite-wasm-http
Dá para fazer busca de texto completo do SQLite sem baixar o banco FTS inteiro. Se o termo de busca for curto o suficiente, você acaba recebendo a maior parte, mas não tudo
https://medium.com/offline-camp/couchdb-pouchdb-and-hoodie-as-a-stack-for-progressive-web-apps-a6078a985f18
Pode ir de uma biblioteca de áudio a software de modelagem 3D. Você cria e persiste as alterações localmente e depois sincroniza com o lado do servidor periodicamente ou quando voltar a ficar online
Esse tipo de app é um bom candidato para um DB embarcado no lado do cliente