開発を進めるとき、「まずローカルで動かして、あとでサーバーに繋ぐ」という流れはよくあります。でもそのとき、こんな問題が起きたことはありませんか?
「ローカル用に書いたコード、サーバー対応に書き直さないといけないじゃん…」
今回紹介する PGlite は、そのストレスをなくすために使っています。
そもそもブラウザのデータ保存って?
ブラウザには標準で IndexedDB というデータ保存の仕組みがあります。ブラウザを閉じてもデータが消えない、サーバー不要で使える、という点では優秀です。
ただ、直接使うのがかなり面倒です。
// IndexedDB を直接使う場合
const tx = db.transaction('locations', 'readwrite');
const store = tx.objectStore('locations');
store.add({ id: '1', name: '自宅' });
// SQLが使えない、キーバリュー形式のみ
SQLは使えない、キーと値のペアしか扱えない——柔軟なデータ操作が必要になった途端に辛くなります。
PGlite とは
PGlite は、ブラウザの中でPostgreSQLを動かすライブラリです。PostgreSQLエンジンをWebAssembly(Wasm)にコンパイルして動かし、データの実際の保存先としてIndexedDBを使います。
あなたのコード(SQL文)
↓
PGlite(PostgreSQLエンジンをWasmで動かす)
↓
IndexedDB(バイナリデータとして実際に保存)
IndexedDBはあくまでバイナリデータの置き場として使われるだけで、SQL解釈やテーブル管理はPGliteが担っています。使う側からは「普通のPostgreSQL」として扱えます。
// PGlite を使う場合
await db.query(`INSERT INTO locations (id, name) VALUES ($1, $2)`, ['1', '自宅']);
await db.query(`SELECT * FROM locations WHERE name = '自宅'`);
なぜ PGlite を選んだのか
一番の理由は、Supabaseとの相性です。
このアプリでは最終的にバックエンドとして Supabase(PostgreSQLベースのBaaS)を使う予定があります。でも開発の初期段階から外部サービスとの連携を考えながら実装するのは、開発スピードを著しく落とします。
そこで考えたのが、こういう方針です。
「最初からSupabaseで使うのと同じSQLを書いておけば、繋ぎ替えるだけで済む」
たとえば、IndexedDBをラップした独自の仕組みでローカル保存を実装してしまうと、後でSupabaseに移行しようとしたときにデータアクセスのコードを全部書き直す羽目になります。
PGliteを使えば、ローカル環境でもSupabaseでも同じPostgreSQL構文のSQLで書けるので、移行時の変更は接続先の切り替えだけ。最初にプラグを用意しておいて、本番では挿すだけ、というイメージです。
SQLって何?
念のため、SQLについて簡単に触れておきます。
SQLはデータベースを操作するための専用言語です。TypeScriptなどのプログラミング言語とは別物ですが、ほぼ英語の文章そのままで読めるのが特徴です。
| 操作 | SQL | 意味 |
|---|---|---|
| 取得 | SELECT | 読む |
| 追加 | INSERT | 書く |
| 更新 | UPDATE | 書き換える |
| 削除 | DELETE | 消す |
MySQL・PostgreSQL・SQLiteなど種類はありますが、基本的な文法はどれもほぼ同じです。一度覚えれば、どの環境でも使い回せます。
PGlite の特徴まとめ
- 端末ごとに独立している(PCとスマホで別々のIndexedDB)
- ブラウザを閉じてもデータは消えない
- アプリをアンインストール(ブラウザのデータ削除)すると消える
- サーバー不要でブラウザだけで動く
- PostgreSQL互換なのでSupabaseへの移行がスムーズ
まとめ
PGliteを使う理由は、単に「ブラウザでSQLが使えて便利」というだけではありません。最初から本番を見据えた設計にしておくことで、後の移行コストをゼロに近づけるという判断です。
開発初期から外部連携を作り込みすぎると手が止まる。でも将来の拡張を無視して実装すると後で詰む。そのバランスをとるための選択として、PGliteはちょうどいい落とし所でした。