SQLite Wasm dans le navigateur reposant sur le système de fichiers privé d'origine

Utilisez SQLite pour gérer efficacement tous vos besoins de stockage sur le Web.

À propos de SQLite

SQLite est un système de gestion de bases de données relationnelles intégré populaire, Open Source et léger. De nombreux développeurs l'utilisent pour stocker des données de manière structurée et facile à utiliser. En raison de sa petite taille et de ses besoins en mémoire, SQLite est souvent utilisé comme moteur de base de données dans les appareils mobiles, les applications de bureau et les navigateurs Web.

L'une des principales caractéristiques de SQLite est qu'il s'agit d'une base de données sans serveur, ce qui signifie qu'elle ne nécessite pas de processus de serveur distinct pour fonctionner. Au lieu de cela, la base de données est stockée dans un seul fichier sur l'appareil de l'utilisateur, ce qui facilite l'intégration dans les applications.

Logo SQLite.

SQLite basé sur Web Assembly

Il existe un certain nombre de versions non officielles de SQLite basées sur Web Assembly (Wasm), ce qui permet de les utiliser dans des navigateurs Web, par exemple sql.js. Le sous-projet WASM/JS sqlite3 est le premier projet qui est officiellement associé au projet SQLite : créer des builds Wasm des membres établis de la bibliothèque de la famille de livrables SQLite compatibles. Les objectifs concrets de ce projet sont les suivants:

  • Liaison d'une API sqlite3 de bas niveau aussi proche de la version C que possible en termes d'utilisation
  • API de niveau supérieur orientée objet, davantage semblable à sql.js et aux implémentations de type Node.js, qui communique directement avec l'API de bas niveau. Cette API doit être utilisée à partir du même thread que l'API de bas niveau.
  • API basée sur des nœuds de calcul qui communique avec les API précédentes via des messages de workers. Celle-ci est destinée à être utilisée dans le thread principal, avec les API de niveau inférieur installées dans un thread de nœud de calcul et à leur parler via des messages de nœud de calcul.
  • Variante de l'API Worker basée sur une promesse, qui masque entièrement les aspects de la communication entre threads à l'utilisateur.
  • Prise en charge du stockage persistant côté client à l'aide des API JavaScript disponibles, y compris OPFS (Origin Private File System).

Utiliser Wasm SQLite avec le backend de persistance du système de fichiers privé d'origine

Installer la bibliothèque depuis npm

Installez le package @sqlite.org/sqlite-wasm depuis npm à l'aide de la commande suivante:

npm install @sqlite.org/sqlite-wasm

Système de fichiers privés d'origine

Le système de fichiers privés d'origine (OPFS, Origin Private File System), qui fait partie de l'API File System Access, est complété par une surface spéciale qui offre un accès très performant aux données. Cette nouvelle surface est différente des surfaces existantes en offrant un accès en écriture sur place et exclusif au contenu d'un fichier. Ce changement, ainsi que la possibilité de lire de manière cohérente les modifications non vidées et la disponibilité d'une variante synchrone sur des nœuds de calcul dédiés, améliore considérablement les performances et débloque de nouveaux cas d'utilisation.

Comme vous pouvez l'imaginer, le dernier point des objectifs du projet, la prise en charge du stockage persistant côté client à l'aide des API JavaScript disponibles, s'accompagne d'exigences de performances strictes concernant la persistance des données dans le fichier de base de données. C'est là qu'interviennent le système de fichiers privés d'origine et, plus précisément, la méthode createSyncAccessHandle() des objets FileSystemFileHandle. Cette méthode renvoie une promesse qui se résout en un objet FileSystemSyncAccessHandle pouvant être utilisé pour lire et écrire de manière synchrone dans un fichier. La nature synchrone de cette méthode offre des avantages en termes de performances, mais elle n'est donc utilisable que dans des web workers dédiés pour les fichiers du système de fichiers privés d'origine. Le thread principal ne peut donc pas être bloqué.

Définir les en-têtes requis

Entre autres fichiers, l'archive SQLite Wasm téléchargée contient les fichiers sqlite3.js et sqlite3.wasm, qui constituent le build WASM/JS de sqlite3. Le répertoire jswasm contient les livrables principaux sqlite3, et le répertoire de premier niveau contient les applications de démonstration et de test. Les navigateurs ne diffusent pas les fichiers Wasm à partir d'URL file://. Toutes les applications que vous créez à l'aide de cette option nécessitent donc un serveur Web. Ce serveur doit inclure les en-têtes suivants dans sa réponse lors de la diffusion des fichiers:

  • Cross-Origin-Opener-Policy défini sur la directive same-origin, qui isole le contexte de navigation exclusivement dans les documents de même origine. Les documents d'origines multiples ne sont pas chargés dans le même contexte de navigation.
  • Cross-Origin-Embedder-Policy défini sur la directive require-corp, de sorte qu'un document ne puisse charger que des ressources de la même origine, ou des ressources explicitement marquées comme pouvant être chargées depuis une autre origine.

La raison de ces en-têtes est que SQLite Wasm dépend de SharedArrayBuffer et que la définition de ces en-têtes fait partie de ses exigences de sécurité.

Si vous inspectez le trafic à l'aide des outils de développement, vous devriez trouver les informations suivantes:

Les deux en-têtes mentionnés ci-dessus, Cross-Origin-Embedder-Policy et Cross-Origin-Opener-Policy, mis en évidence dans les outils pour les développeurs Chrome.

Test de débit

L'équipe SQLite a exécuté des analyses comparatives sur son implémentation WebAssembly par rapport au Web SQL obsolète. Ces analyses comparatives montrent que SQLite Wasm est généralement aussi rapide que Web SQL. Parfois, c'est un peu plus lent, parfois un peu plus rapide. Consultez tous les détails sur la page des résultats.

Exemple de code de démarrage

Comme indiqué précédemment, SQLite Wasm avec le backend de persistance du système de fichiers privé d'origine doit s'exécuter à partir d'un contexte de nœud de calcul. La bonne nouvelle est que la bibliothèque s'occupe automatiquement de tout cela pour vous et que vous pouvez l'utiliser directement à partir du thread 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);
  }
})();

Démonstration

Observez le code ci-dessus en action dans la démonstration. Veillez à consulter le code source sur Glitch. Notez que la version intégrée ci-dessous n'utilise pas le backend OPFS, mais que c'est le cas lorsque vous ouvrez la version de démonstration dans un onglet distinct.

Déboguer le système de fichiers privés d'origine

Pour déboguer la sortie Origin Private File System de SQLite Wasm, utilisez l'extension Chrome OPFS Explorer.

OPFS Explorer dans le Chrome Web Store.

Après avoir installé l'extension, ouvrez les outils pour les développeurs Chrome, sélectionnez l'onglet Explorateur OPFS. Vous êtes alors prêt à inspecter ce que SQLite Wasm écrit dans le système de fichiers privé d'origine.

Extension Chrome OPFS Explorer affichant la structure du système de fichiers privé d&#39;origine de l&#39;application de démonstration.

Si vous cliquez sur l'un des fichiers de la fenêtre de l'explorateur OPFS dans les outils de développement, vous pouvez l'enregistrer sur le disque local. Vous pouvez ensuite utiliser une application telle que SQLite Viewer pour inspecter la base de données. Vous pouvez ainsi vous assurer que SQLite Wasm fonctionne réellement comme prévu.

Application de la visionneuse SQLite utilisée pour ouvrir un fichier de base de données à partir de la démonstration de SQLite Wasm.

Obtenir de l'aide et envoyer des commentaires

SQLite Wasm est développé et géré par la communauté SQLite. Pour obtenir de l'aide et nous faire part de vos commentaires, effectuez une recherche dans le forum d'assistance et publiez-le. La documentation complète est disponible sur le site de SQLite.

Remerciements

Image principale de Tobias Fischer sur Unsplash.