サーバー不要のPostgres:ブラウザとNode.jsでPGliteを動かす

Database tutorial - IT technology blog
Database tutorial - IT technology blog

午前2時30分のデータベース崩壊

火曜日の深夜、私の開発環境が行き詰まりました。複雑なマイグレーションのデバッグをしていましたが、Docker Desktopが「Starting」ループで固まってしまい、ノートPCのファンが5,000 RPMで回転し、一向に動きません。たった3つのSQLクエリを実行するためだけに、コンテナを削除したりデータベースエンジンを再インストールしたりして夜を明かしたくはありませんでした。私が必要としていたのは、重厚なPostgresサーバーなしで利用できる、Postgresの論理的なパワーでした。

長年、MySQL、Postgres、MongoDBを使い分けてきましたが、どのツールにも適材適所がある一方で、私のプロジェクトにおいてPostgresの厳格なスキーマとJSONBサポートは通常譲れないものです。しかし、ローカルインスタンスを管理するオーバーヘッドは、常に生産性の足かせとなっていました。このフラストレーションが、私をPGliteへと導いたのです。

現実的な課題:Postgres導入の摩擦

ほとんどの開発者がこのボトルネックに直面したことがあるはずです。ちょっとしたCLIツールや、概念実証(PoC)のデモ、あるいはローカルファーストなWebアプリを作りたいとき、これまでは選択肢が限られており、どれも妥協が必要でした。50行のスクリプトには過剰なフル機能のPostgreSQLサーバーをインストールするか、JSONBインデックスや特定のPostgres演算子が欠けているSQLiteを使うか、あるいはリクエストごとに150msのレイテンシが発生するリモートのクラウドDBに接続するかです。

この摩擦は、特に「ローカルファースト」なソフトウェアを構築する際に痛感されます。ブラウザ内で動作し、後でバックエンドと同期するデータベースが必要な場合、通常はSQLite WASMが候補に挙がります。しかし、本番環境のバックエンドがPostgresである場合、構文の違いを処理するために煩雑な変換レイヤーを書くことになります。これは、本番環境でしか発生しないバグを生む原因となります。

なぜ標準のPostgresは軽量環境で苦戦するのか

PostgreSQLはクライアント・サーバー時代に設計されました。専用のメモリ管理、複雑なファイルシステムロック、アクティブなネットワークリスナーを備えた、長時間実行されるプロセスであることを前提としています。そのアーキテクチャを一時的なテストランナーやブラウザのタブに押し込もうとすると、システムが拒絶反応を起こします。そもそも、一時的な(エフェメラルな)利用を想定して作られていないのです。

標準のPostgresバイナリは多くの場合100MBを超え、特定のオペレーティングシステム向けにコンパイルされています。それらをブラウザで実行するには、通常、重い仮想マシンやコンテナが必要になります。そのため、以下のような現代的なユースケースには不向きです:

  • ミリ秒単位で起動する即時利用可能な開発環境。
  • ユーザーがブラウザで直接SQLを実行できるインタラクティブなドキュメント。
  • バックエンドへの往復なしに関係の整合性を必要とするクライアントサイドアプリ。

代替案の検討

より良い方法を探していた深夜、私は3つの一般的な回避策を検討しました:

  1. SQLite WASM: 高速で信頼性がありますが、pgvectorや私のアプリが必要とする特定のダイアレクト(方言)をサポートしていません。
  2. Docker経由の埋め込みPostgres: 動作はしますが、5〜10秒の起動時間はユニットテストの勢いを削いでしまいます。
  3. DBのモック化: 高速ですが、モックでは実際のSQL実行における微妙なエッジケースを捉えることができず、誤った安心感を与えてしまいます。

そこで見つけたのがPGliteです。これはPostgreSQL 15/16をWebAssembly (WASM) にビルドし、3.7MB(gzip圧縮時)のパッケージに凝縮したものです。エミュレーションではなく、Node.jsプロセスやブラウザのタブ内で直接動作する、外部依存関係ゼロの本物のPostgresエンジンです。

実装:PGliteのデプロイ方法

PGliteは外科的な解決策を提供します。ネットワークレイヤーを取り除き、それを直接的な内部APIに置き換えます。私のプロジェクトを救うためにどのように使用したかを紹介します。

1. Node.jsでの即時セットアップ

apt-getbrew installコマンドは一切不要です。バックエンドスクリプトや一時的なテストスイートなら、セットアップは一行で済みます。

npm install @electric-sql/pglite

インメモリデータベースの初期化は非常にシンプルです:

import { PGlite } from "@electric-sql/pglite";

const db = new PGlite();

// テーブルの作成とデータの挿入
await db.query("CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT, data JSONB);");
await db.query("INSERT INTO users (name, data) VALUES ($1, $2);", [
  "John Doe",
  { role: "admin", permissions: ["read", "write"] },
]);

// クエリの実行
const result = await db.query("SELECT * FROM users WHERE data->>'role' = 'admin';");
console.log(result.rows);

2. ブラウザ上でのPostgresとデータの永続化

本当の魔法はブラウザで起こります。PGliteはIndexedDBを使用してデータを永続化できるため、ページをリフレッシュしてもデータベースの内容が保持されます。これは、オフライン対応のダッシュボードや複雑なローカルツールに最適です。

import { PGlite } from "@electric-sql/pglite";

// 'idb://' プレフィックスが永続化を自動的に処理します
const db = new PGlite("idb://my-local-store");

async function runPerformanceCheck() {
  const start = performance.now();
  await db.query("SELECT 1;");
  const end = performance.now();
  console.log(`コールドスタートクエリの所要時間: ${Math.round(end - start)}ms`);
}

3. エクステンションによるベクトル検索の追加

多くの開発者がPostgresを選ぶ理由は、そのエコシステムにあります。PGliteはpgvectorのようなエクステンションをサポートしており、これはローカルでのRAG(検索拡張生成)アプリケーションやAIの実験に不可欠です。

import { PGlite } from "@electric-sql/pglite";
import { vector } from "@electric-sql/pglite/vector";

const db = new PGlite({
  extensions: { vector }
});

// ベクトルエクステンションの有効化とデータの操作
await db.query('CREATE EXTENSION IF NOT EXISTS vector;');
await db.query('CREATE TABLE items (id SERIAL, embedding vector(3));');
await db.query('INSERT INTO items (embedding) VALUES ("[1.1, 2.2, 3.3]");');

// 近傍探索の実行
const res = await db.query('SELECT * FROM items ORDER BY embedding <-> "[1,2,3]" LIMIT 1;');
console.log("最近傍一致:", res.rows);

ワークフローをどう変えるか

PGliteは単なる巧妙な技術的成果以上のものです。コードの検証方法を根本から変えます。VitestやJestでデータベースをモック化する代わりに、テストファイルごとに本物のPostgresインスタンスを実行できます。100ms未満で起動するため、本番環境と100%同じ精度を保ちながら、テストスイートの高速性を維持できます。

最近では「プレビュー環境」にも活用し始めました。URLを共有するだけで、動作するデータベースを含めたフル機能のアプリをステークホルダーに提供できます。RDSのコストもかからず、管理すべきバックエンドのインフラもありません。すべてはブラウザが処理してくれます。

まとめ:ポータブル・データベースの新時代

深夜に壊れたターミナルを見つめているとき、必要なのは複雑さではなく、邪魔にならないツールです。PGliteは「Postgresは重い」という固定観念を払拭してくれます。

これまで検討もされなかったような環境で、世界で最も信頼されているリレーショナルデータベースを使用できるようになります。CLIユーティリティ、高速なテストスイート、あるいはローカルファーストなWebアプリを構築しているかに関わらず、PGliteは開発者体験を大幅に向上させます。それは私の夜を救ってくれました。そして、あなたの次のプロジェクトも救ってくれるはずです。

Share: