SQLite Wasm no navegador com suporte do sistema de arquivos particulares de origem

Use o SQLite para processar todas as suas necessidades de armazenamento de maneira eficiente na Web.

Sobre o SQLite

O SQLite é um conhecido sistema de gerenciamento de banco de dados relacional, de código aberto, leve e incorporado. Muitos desenvolvedores o usam para armazenar dados de maneira estruturada e fácil de usar. Devido ao tamanho pequeno e aos poucos requisitos de memória, o SQLite é frequentemente aproveitado como um mecanismo de banco de dados em dispositivos móveis, aplicativos para computador e navegadores da Web.

Um dos principais recursos do SQLite é que ele é um banco de dados sem servidor, o que significa que ele não requer um processo de servidor separado para operar. Em vez disso, o banco de dados é armazenado em um único arquivo no dispositivo do usuário, facilitando a integração com aplicativos.

Logotipo do SQLite.

SQLite baseado em Assembly da Web

Há várias versões não oficiais do SQLite baseadas no Web Assembly (Wasm), permitindo que ele seja usado em navegadores da Web, como sql.js. O subprojeto sqlite3 WASM/JS é o primeiro esforço oficialmente associado ao projeto SQLite (link em inglês) para fazer builds do Wasm da biblioteca membros estabelecidos da família de entregas do SQLite com suporte. As metas concretas desse projeto incluem:

  • Vincule uma API sqlite3 de baixo nível que seja o mais próximo possível da API C em termos de uso.
  • Uma API orientada a objetos de nível superior, mais semelhante a sql.js e implementações no estilo Node.js, que se refere diretamente à API de nível inferior. Essa API precisa ser usada na mesma linha de execução que a API de nível inferior.
  • Uma API baseada em worker que se comunica com as APIs anteriores por mensagens de worker. Ela se destina ao uso na linha de execução principal, com as APIs de nível inferior instaladas em uma linha de execução de worker e se comunicando com elas por mensagens de worker.
  • Uma variante baseada em promessas da API Worker que oculta totalmente os aspectos de comunicação entre linhas de execução do usuário.
  • Suporte para armazenamento permanente do lado do cliente usando APIs JavaScript disponíveis, incluindo o sistema de arquivos particulares de origem (OPFS, na sigla em inglês).

Como usar o SQLite Wasm com o back-end de persistência do sistema de arquivos privados de origem

Como instalar a biblioteca pelo npm

Instale o pacote @sqlite.org/sqlite-wasm (link em inglês) do npm com o seguinte comando:

npm install @sqlite.org/sqlite-wasm

O sistema de arquivos particulares de origem

O sistema de arquivos privados de origem (OPFS, na sigla em inglês, parte da API File System Access) é aumentado com uma superfície especial que oferece acesso de alto desempenho aos dados. Essa nova plataforma é diferente das atuais, oferecendo acesso de gravação exclusivo e no local ao conteúdo de um arquivo. Essa mudança, junto com a capacidade de ler consistentemente as modificações não transferidas e a disponibilidade de uma variante síncrona em workers dedicados, melhora significativamente o desempenho e desbloqueia novos casos de uso.

Como você pode imaginar, o último ponto dos objetivos do projeto, o suporte para armazenamento permanente do lado do cliente usando APIs JavaScript disponíveis, vem com requisitos de desempenho rigorosos em relação à persistência de dados no arquivo do banco de dados. É aqui que o sistema de arquivos privados de origem e, mais especificamente, o método createSyncAccessHandle() de objetos FileSystemFileHandle entram em jogo. Esse método retorna uma promessa que é resolvida em um objeto FileSystemSyncAccessHandle que pode ser usado para ler e gravar em um arquivo de forma síncrona. A natureza síncrona desse método traz vantagens de desempenho, mas, portanto, ele só pode ser usado dentro de Web Workers dedicados para arquivos no sistema de arquivos privados de origem. Assim, a linha de execução principal não pode ser bloqueada.

Como definir os cabeçalhos necessários

Entre outros arquivos, o arquivo SQLite Wasm transferido por download contém os arquivos sqlite3.js e sqlite3.wasm, que compõem o build sqlite3 WASM/JS. O diretório jswasm contém os principais resultados do sqlite3, e o diretório de nível superior contém apps de demonstração e teste. Os navegadores não disponibilizarão arquivos Wasm de URLs file://. Portanto, todos os apps criados com isso exigem um servidor da Web, que precisa incluir os seguintes cabeçalhos na resposta ao exibir os arquivos:

O motivo desses cabeçalhos é que o SQLite Wasm depende de SharedArrayBuffer, e a configuração desses cabeçalhos faz parte dos requisitos de segurança.

Se você inspecionar o tráfego com o DevTools, encontrará as seguintes informações:

Os dois cabeçalhos mencionados acima, Cross-Origin-Embedder-Policy e Cross-Origin-Opener-Policy, estão destacados no Chrome DevTools.

Teste de velocidade

A equipe do SQLite executou algumas comparações na implementação do WebAssembly com o SQL da Web descontinuado. Essas comparações mostram que o SQLite Wasm geralmente é tão rápido quanto o Web SQL. Às vezes é um pouco mais lento, às vezes é um pouco mais rápido. Confira todos os detalhes na página de resultados.

Exemplo de código para começar

Como mencionado anteriormente, o SQLite Wasm com o back-end de persistência do sistema de arquivos privados de origem precisa ser executado em um contexto de worker. A boa notícia é que a biblioteca cuida de tudo isso automaticamente, e você pode usá-la diretamente na linha de execução principal.

import { sqlite3Worker1Promiser } from '@sqlite.org/sqlite-wasm';

(async () => {
  try {
    console.log('Loading and initializing SQLite3 module...');

    const promiser = await new Promise((resolve) => {
      const _promiser = sqlite3Worker1Promiser({
        onready: () => {
          resolve(_promiser);
        },
      });
    });

    console.log('Done initializing. Running demo...');

    let response;

    response = await promiser('config-get', {});
    console.log('Running SQLite3 version', response.result.version.libVersion);

    response = await promiser('open', {
      filename: 'file:worker-promiser.sqlite3?vfs=opfs',
    });
    const { dbId } = response;
    console.log(
      'OPFS is available, created persisted database at',
      response.result.filename.replace(/^file:(.*?)\?vfs=opfs$/, '$1'),
    );

    await promiser('exec', { dbId, sql: 'CREATE TABLE IF NOT EXISTS t(a,b)' });
    console.log('Creating a table...');

    console.log('Insert some data using exec()...');
    for (let i = 20; i <= 25; ++i) {
      await promiser('exec', {
        dbId,
        sql: 'INSERT INTO t(a,b) VALUES (?,?)',
        bind: [i, i * 2],
      });
    }

    console.log('Query data with exec()');
    await promiser('exec', {
      dbId,
      sql: 'SELECT a FROM t ORDER BY a LIMIT 3',
      callback: (result) => {
        if (!result.row) {
          return;
        }
        console.log(result.row);
      },
    });

    await promiser('close', { dbId });
  } catch (err) {
    if (!(err instanceof Error)) {
      err = new Error(err.result.message);
    }
    console.error(err.name, err.message);
  }
})();

Demonstração

Confira o código acima em ação na demonstração. Confira o código-fonte no Glitch. Observe como a versão incorporada abaixo não usa o back-end do OPFS. No entanto, quando você abre a demonstração em uma guia separada, ela usa.

Como depurar o sistema de arquivos privados de origem

Para depurar a saída do sistema de arquivos privados de origem do SQLite Wasm, use a extensão do Chrome OPFS Explorer.

Explorador do OPFS na Chrome Web Store.

Depois de instalar a extensão, abra o Chrome DevTools, selecione a guia OPFS Explorer e você estará pronto para inspecionar o que o SQLite Wasm grava no sistema de arquivos particulares de origem.

Extensão OPFS Explorer do Chrome mostrando a estrutura do sistema de arquivos particulares de origem do app de demonstração.

Se você clicar em qualquer um dos arquivos na janela do OPFS Explorer no DevTools, poderá salvá-lo no disco local. Você pode usar um app como o SQLite Viewer para inspecionar o banco de dados e garantir que o SQLite Wasm realmente funcione conforme prometido.

App SQLite Viewer usado para abrir um arquivo de banco de dados pela demonstração do SQLite Wasm.

Receber ajuda e enviar feedback

O SQLite Wasm é desenvolvido e mantido pela comunidade do SQLite. Para receber ajuda e enviar feedback, pesquise e poste no fórum de suporte. A documentação completa está disponível no site do SQLite.

Agradecimentos

Imagem principal de Tobias Fischer no Unsplash (links em inglês).