あなたは、MySQL派ですか?、PostgreSQL派ですか?
私が最初に使ったデータベースはOracle v5ですが、独立してからはPostgreSQLをメインで使ってきました。
今まで本格的なデータベースというとサーバーで起動し、TCP/IP等で接続して使うイメージでした。
しかし最近、Postgres WASMをTypeScript/JavaScriptから呼び出せるようにしたPGliteがリリースされました。PGliteは、Node.jsやBunそしてWebブラウザ上で実行可能です!!
いつものジャンケンアプリに組み込んでみた
早速いつものジャンケンReactアプリに組み込んでみました。
PGLiteのインストールは npm install @electric-sql/pglite
でインストールできます。コードはTauriのブログとほぼ同じです。
変更があったのは、メインのJyankenコンポーネントのみで以下のようになっています。
- ① PostgreSQLとの接続
idb://・・・
を指定する事で、ストレージにはブラウザーのIndexedDBを使います
- ② ジャンケンの結果を挿入するSQL文の実行
- 当然ですがプレースフォルダーが使えます
- ③ scoresテーブル作成のSQL文の実行
IF NOT EXISTS
があるのでテーブルが存在しない場合のみ作成されます
- ④ scoresテーブルから全データを取得するSQL文の実行
- 取得されるデータの型ScoreTypeをテンプレートで指定しています
- 取得されたScoreType型の配列がresult.rowsに入っています
"use client";
import { useEffect, useState } from 'react';
import { PGlite } from '@electric-sql/pglite';
const db = new PGlite('idb://my-pgdata'); // ← ①
export default function Jyanken () {
const [scores, setScores] = useState<ScoreType[]>([]);
const pon = async (human: Te) => {
const computer = Math.floor(Math.random() * 3) as Te;
const judgment = (computer - human + 3) % 3 as Te;
const score: ScoreType = {human, computer, judgment};
await db.query( // ← ②
`INSERT INTO scores (human, computer, judgment) VALUES ($1, $2, $3)`,
[human, computer, judgment]);
setScores([score, ...scores]);
}
useEffect(() => {
(async() => {
await db.exec( // ← ③
`CREATE TABLE IF NOT EXISTS scores (
id SERIAL PRIMARY KEY,
human INTEGER NOT NULL,
computer INTEGER NOT NULL,
judgment INTEGER NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)`);
const result = await db.query<ScoreType>( // ← ④
`SELECT human, computer, judgment from scores ORDER BY id DESC`
);
setScores(result.rows);
})();
}, []);
return (
<div className="md:ml-8">
<h1 className="my-4 ml-4 text-3xl font-bold">じゃんけん ポン!</h1>
<div className="p-3 md:p-6 bg-white md:w-3/5">
<JyankenBox pon={pon} />
<ScoreBox scores={scores} />
</div>
</div>
);
}
これだけです。簡単ですね!
ブラウザーを再起動しても、開発サーバーを再起動してもデータは残っています。
IndexedDBの中身
下の画像はIndexedDBのデータファイルの一部ですが、通常のサーバーで動いている/var/lib/postgresql/data/
の下にあるファイルのパス名がIndexedDBのキーになっています。対応するIndexedDBの値がサーバーのファイルに対応していると思われます。
IndexedDBには仕様上の容量制限はないようですが、このブログによるとPCでは10GBくらいまで保存できるようです。
PGliteの動作原理ですが、ホームページのHow it worksによると
PostgreSQLは通常プロセスのフォークモデルを使用して動作します。クライアントが接続を開始すると、その接続を管理するために新しいプロセスがフォークされます。しかし、C to WebAssembly (WASM)コンパイラであるEmscriptenでコンパイルされたプログラムは新しいプロセスをフォークすることができず、厳密にシングルプロセスモードで動作します。その結果、PostgreSQLをWASMに直接コンパイルして従来の動作をさせることはできません。幸いなことに、PostgreSQLには、起動時や復旧時のコマンドライン使用を主目的とした “シングルユーザモード “があります。この機能を基に、PGliteはJavaScript環境内でWASMにコンパイルされたPostgreSQLとの対話を容易にする入出力経路を導入しています。 DeepLによる翻訳です
psqlコマンドが欲しいぞ
今回の使い方は単純なので直ぐ動きましたが、本格的なアプリでは多数のテーブルがあり、途中でテーブルの値を確認たり変更したくなりますよね。
その場合は、PGlite REPL React Componentを使えば良いようです。
npm install @electric-sql/pglite-repl
でインストールし、ここではnext.jsを使っているのでapp/psql/page.tsx
ファイルを作成し、以下のコードを書きました。
"use client";
import { PGlite } from "@electric-sql/pglite";
import { Repl } from "@electric-sql/pglite-repl";
export default function SqlTool() {
const pg = new PGlite('idb://my-pgdata');
return (
<>
<h2>PGlite SQL tool</h2>
<Repl pg={pg} />
</>
);
}
http://localhost:3000/psql
をアクセスすると下の画像のようにpsqlのようにSQL文の実行やメタコマンドの実行できます。めでたしめでたし 😁
まとめ
PGliteを使うとブラウザー上でちゃんとしたPostgreSQLが動きます!
今までサーバー・クライアントで動かしていたWebアプリを、ブラウザー(+HTML/CSS, JavaSciptを配信するWebサーバー)で動くアプリに変換できるので新たなタイプのアプリが作れそうですね。