2026年3月13日金曜日

Web Storage上で直接SQLを実行するライブラリは原則存在しません。

 

  • Web Storage上で直接SQLを実行するライブラリは原則存在しません。localStorage/sessionStorageは単純な同期的キー・バリュー層であり、SQL処理に必要なトランザクションやインデックス、非同期処理などを持たないためです。過去にWebSQL(SQLiteベース)が提案されましたが、現在は非推奨・削除されています。唯一に近いものは、SQLiteをWASM化してlocalStorageをVFSとして使う仕組み(SQLite公式WASMの「kvvfs」)など特殊な実装例です
  • 代替として、IndexedDBベースのSQL実行メモリDB+永続化が使われます。代表例はsql.js(SQLiteのWASM版、メモリDBでインポート/エクスポートで永続化)absurd-sql(sql.js+IndexedDBバックエンド。SQLiteファイルをIndexedDBにチャンク保存)PGlite(WASM版Postgres、インメモリ&IndexedDB永続化対応)などです。これらはSQL互換性が高く、トランザクションや性能もSQLiteレベルですが、IndexedDBやOPFSなどを用いるためブラウザサポートやサイズ制限に注意が必要です。
  • WebSQL(旧仕様)は現在サポート外です。Chromium系では2022年に非推奨、2024年に完全削除されました。WebSQLの代替として、WASM版SQLite(OPFSバックエンド)やIndexedDBベースのSQLライブラリが公式に推奨されています
  • 技術的理由(localStorageがSQLに不向きな理由): localStorageは「同期的」「単一キー・バリュー」「5~10MB程度のサイズ制限」「トランザクションや原子性がない」「Worker未対応」など、多くのSQL必須機能が欠けます。例えば複数行挿入の原子性を保証できず、複雑な問い合わせができず、データ型も文字列のみなどです。
  • 関連議論: StackOverflowやGitHubでも「localStorageでSQLiteをエミュレートできるか」という質問が複数あります。StackOverflowではsql.jsやalasql、SequelSphere等が提案例として挙がっています。TypeORMのIssueでは、sql.jsのローカル保存が同期処理でパフォーマンス問題を起こす例も報告されています
  • 比較表: 以下に主要なアプローチの比較を示します。
名称Web Storage直接?永続化バックエンドSQL互換性WASM対応ブラウザ長所・短所例・参照
sql.jsいいえメモリ(エクスポート可能)、<br>IndexedDB(手動)SQLite✔(WASM版)主要ブラウザ標準SQL完全対応。サーバ不要。<br>永続化は自前実装(エクスポート→IDB/LocalStorageなど)。同期処理で大容量は遅い。npm:sql.js<br>GitHub: sql-js/sql.js
absurd-sqlいいえIndexedDB (SQLiteFS)<br>(SharedArrayBuffer必要)SQLite✔(WASM版)Chrome, Firefox等SQL.js上にIndexedDB保存機能追加。高速(IndexedDBより約10倍速)。Worker内で実行。対応ブラウザ要件が厳しい(COOP/COEP)GitHub: jlongster/absurd-sql
PGliteいいえIndexedDB (ブラウザ)<br>ファイル(Node/Deno)PostgreSQL準拠✔(WASM版)主要ブラウザ、Node.js等本物のPostgresをWASM化。リアルタイム更新や拡張機能(pgvector等)対応。サーバ不要。<br>シングルコネクション制限。ビルドが重い。GitHub: electric-sql/pglite
WebSQLはい(歴史的には)SQLite(内部)SQL (SQLite 3.6.19)✔(C++コア)Chrome(旧), SafariかつてHTML5標準として提案されたSQLiteベースDB。<br>現在はChromium廃止、Firefox未実装で事実上死に仕様。トランザクションありだがCallback APIで古い。W3C: Web SQL Database仕様
localStorageDBはい(疑似DBレイヤ)localStorage/sessionStorage独自構造(SQLではない)×主要ブラウザlocalStorage上にテーブル概念を実装した軽量ライブラリ。クエリ機能も備えるが内部はJSON保存。トランザクション・並列性なし。メンテナンス停滞(最終更新2018)GitHub: knadh/localStorageDB
alasqlいいえ(選択可)メモリ(配列構造)、<br>手動でLocalStorage等へバックアップ可SQL-ish(一部機能制限)×主要ブラウザJavaScript製SQLエンジン。クライアント/Node両対応。LocalStorageに手動保存サンプルあり。トランザクション機能は限定的。メモリ上のテーブルデータを直接操作。GitHub: alasql/alasql<br>(例:
jSQLいいえcookies, localStorage, IndexedDB, WebSQL(順選択)SQL-ish×主要ブラウザMySQLライクなSQLエンジン。各種ストレージにフォールバック可能。しかし最終更新2018と古い。GitHub: Pamblam/jSQL

(3) localStorageの技術的制約

  • 同期的API: localStorage/sessionStorageはいずれも同期処理であり、操作するとJavaScript実行がブロックされます。大きなデータ操作ではUIのフリーズを招きます
  • トランザクション・原子性なし: 内部は単一キー毎の書き込みで、複数操作を原子化できません。ロールバック機能や排他制御がなく、複雑な更新時に整合性が保てません。
  • 単純なKey/Valueモデル: キーは文字列、値も文字列でしか保持できません。SQLite等が用意するBLOBや数値型等がなく、複雑なオブジェクトはJSON化が必要です。
  • 容量制限: 多くのブラウザで約5MB程度に制限されています(2バイト文字コーディングのため実質4MB以下)。これを超えると書き込み失敗します。
  • ワーカ利用不可: localStorageはメインスレッド専用で、Web Workerからアクセスできません。バックグラウンド処理に不向きです。
  • クロスタブ/セキュリティ: 同一オリジン内で共有されますが、同時書き込み時の制御がなく、片方のタブで更新しても他タブに自動通知されるのみです(競合防止機構なし)。またXSSに弱く、セキュアなデータ格納には不適切です

(4) 参考議論例

  • StackOverflow: 「localStorageでSQLiteをエミュレートするライブラリ」は何か?という質問では、sql.jsやalasql、SequelSphereなどが回答として挙がりました。alasqlは自身でSQLエンジンを持ち、手動でlocalStorageにエクスポート/インポートする例が示されています。また、TypeORMのIssueでは、sql.jsドライバがlocalStorage同期保存のため起動が数秒停止すると報告され、IndexedDB(localForage)に差し替える話が出ました
  • GitHub Issues: 「Web SQLは非推奨にすべきか」というディスカッションや、「sql.jsドライバのIndexedDB対応」など、多数の議論があります。例えばElectron-SQLiteプロジェクトではWebSQL削除を検討中です

(6) コード例

  • sql.js+IndexedDB永続化(簡易例)

    js
    import initSqlJs from 'sql.js';
    // SQL.jsモジュール読み込み
    const SQL = await initSqlJs({ locateFile: file => file });
    // 新規DB作成
    const db = new SQL.Database();
    db.run("CREATE TABLE users(id INTEGER, name TEXT);");
    db.run("INSERT INTO users VALUES(1, 'Alice'),(2, 'Bob');");
    // DBファイルをバイト配列化
    const data = db.export(); // Uint8Array
    // IndexedDBに保存
    const request = indexedDB.open('sqljsDB', 1);
    request.onupgradeneeded = e => {
      e.target.result.createObjectStore('files');
    };
    request.onsuccess = e => {
      const txn = e.target.result.transaction('files', 'readwrite');
      txn.objectStore('files').put(data, 'mydb.sqlite');
    };
    

    この例では、sql.jsでDBを操作し、db.export()で得たバイト列をIndexedDBに保存しています。IndexedDBにより非同期で永続化できますが、読み込みや再インポートも自前実装が必要です。

  • absurd-sql基本セットアップ

    js
    // main.js(UIスレッド)
    import { initBackend } from 'absurd-sql/dist/indexeddb-main-thread';
    const worker = new Worker('worker.js', { type: 'module' });
    initBackend(worker);
    // worker.js
    import initSqlJs from '@jlongster/sql.js';
    import { SQLiteFS } from 'absurd-sql';
    import IndexedDBBackend from 'absurd-sql/dist/indexeddb-backend';
    const SQL = await initSqlJs({ locateFile: file => file });
    const sqlFS = new SQLiteFS(SQL.FS, new IndexedDBBackend());
    SQL.register_for_idb(sqlFS);
    SQL.FS.mkdir('/sql');
    SQL.FS.mount(sqlFS, {}, '/sql');
    const db = new SQL.Database('/sql/db.sqlite', { filename: true });
    db.run("CREATE TABLE test(a); INSERT INTO test VALUES(42);");
    await db.close();
    

    absurd-sqlでは、IndexedDBBackendを使いSQLiteFSを構築して**/sql/db.sqlite**のようにマウントします。この例ではデータがIndexedDB上にチャンク単位で保存され、db操作後は永続化されます。注意点として、SharedArrayBufferの有無でSafari等対応差異があります。

  • localStorageにトランザクションがない例

    js
    // localStorage上で「トランザクション風処理」を試みる例(実際は失敗例)
    try {
      localStorage.setItem('a', JSON.stringify({x:1}));
      // 途中でエラーが発生 (例: JSONシリアライズ失敗など)
      throw new Error("Something went wrong");
      localStorage.setItem('b', JSON.stringify({y:2}));
    } catch(e) {
      // ロールバックできないため 'a' は残り続ける
      console.log(localStorage.getItem('a')); // {"x":1}
      console.log(localStorage.getItem('b')); // null
    }
    

    このようにlocalStorageにはトランザクション管理機能がないため、途中で失敗すると部分的にしかデータが書き込まれず、データ不整合の恐れがあります。

    参考資料

    主要情報は以下を参照しました。

    • SQLite公式ドキュメント – WebAssembly版SQLiteにおけるlocalStorage/sessionStorage対応(kvvfs)
    • absurd-sql GitHubリポジトリREADME
    • PGlite GitHubリポジトリREADME
    • sql.js GitHubリポジトリREADME
    • MDN Web Storage API解説
    • Chrome Developersブログ “Web SQLの非推奨化”
    • StackOverflow Q&A「localStorageでSQLiteをエミュレートするライブラリは?」
    • TypeORM GitHub Issue (#3554) – sql.jsのローカル保存が同期的で遅い問題

    参考リンク(上位8件): SQLite公式Persistent Storage(WASM)、Chrome Developersブログ、sql.js公式README、absurd-sql README、PGlite README、MDN Web Storage API、StackOverflow例、localStorageDB README