はじめに
個人的に、PGliteは「ブラウザでデータベースを扱う」という概念を変えたライブラリだと思っています。
GitHubで13,900スター以上、npmパッケージは月間950万ダウンロード超え。ElectricSQLが開発した「WebAssemblyで動くPostgreSQL」という触れ込みなんですが、これが想像以上に実用的。
正直なところ、最初は「ブラウザでPostgres?おもちゃでしょ?」と思っていたんですよ。IndexedDBやSQLite(sql.js)で十分じゃない?と。でも実際に触ってみたら、「本物のPostgreSQLがそのまま動く」という事実に驚きました。30代になって思うのは、データベースの知識は使い回せるに越したことはないということ。PostgreSQLの文法がそのまま使えるのは、学習コスト的にかなり嬉しい。
PGliteとは
PGliteはWebAssemblyにコンパイルされたPostgreSQLで、TypeScript/JavaScriptライブラリとして提供されています。
ブラウザ、Node.js、Bun、Denoで動作し、外部依存なしで完全なPostgreSQLインスタンスを実行できます。圧縮時わずか3MB以下という軽量さで、これが本当にPostgreSQLなのかと疑うレベル。
従来の「ブラウザでPostgres」系のソリューションは、Linux VMを内部で動かすような重い実装が多かったんですが、PGliteは純粋なWASMコンパイル。Postgresの「シングルユーザーモード」を活用して、JavaScriptとデータベースエンジンの間にI/Oインターフェースを構築しています。
ライセンスはApache 2.0とPostgreSQL Licenseのデュアルライセンス。商用利用も問題なし。
特徴・メリット
1. 本物のPostgreSQLが動く
これ、意外と重要なんですよ。IndexedDBはKey-Value的な使い方しかできないし、sql.jsはSQLite。
PGliteはPostgreSQLなので、JSONBカラム、配列型、CTEなど、PostgreSQL固有の機能がそのまま使える。サーバー側と同じクエリが書けるのはコスパ的に最高です。
2. 拡張機能に対応
pgvectorを含む複数のPostgres拡張機能をサポート。ベクトル検索がブラウザでできるということは、AIアプリのローカル処理も視野に入る。
動的拡張ロードの仕組みがあるので、今後も対応拡張は増えていくはず。
3. 柔軟な永続化オプション
- ブラウザ: IndexedDBに永続化
- Node.js/Bun/Deno: ファイルシステムに永続化
- インメモリ: 一時的なデータ処理用
用途に応じて使い分けられる。特にブラウザでIndexedDBに永続化できるのは、オフラインファーストなPWAを作るときに便利。
4. 軽量
3MB以下(gzip圧縮時)。これでPostgreSQLが動くのは驚き。
初期ロードは発生するものの、CDN経由で配信すればキャッシュも効く。実用上は問題ないレベル。
5. リアクティブ対応
ライブクエリのプリミティブが組み込まれている。データの変更を監視して、UIを自動更新するようなパターンが簡単に実装できる。
インストール方法
npm/yarn/pnpm
npm install @electric-sql/pglite
これだけ。追加の依存関係は不要。
CDN経由(ブラウザ直接)
<script type="module">
import { PGlite } from 'https://cdn.jsdelivr.net/npm/@electric-sql/pglite/dist/index.js'
</script>
バンドラーなしでも使える。プロトタイピングには便利。
基本的な使い方
初期化
import { PGlite } from '@electric-sql/pglite'
// インメモリデータベース
const db = new PGlite()
// IndexedDBに永続化(ブラウザ)
const db = new PGlite('idb://my-database')
// ファイルシステムに永続化(Node.js)
const db = new PGlite('./data/my-database')
初期化はシンプル。パスを指定するだけで永続化先が切り替わる。
クエリの実行
// テーブル作成
await db.exec(`
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE,
created_at TIMESTAMP DEFAULT NOW()
)
`)
// データ挿入
await db.exec(`
INSERT INTO users (name, email) VALUES ('田中太郎', 'tanaka@example.com')
`)
// データ取得
const result = await db.query(`
SELECT * FROM users WHERE name LIKE $1
`, ['田中%'])
console.log(result.rows)
// [{ id: 1, name: '田中太郎', email: 'tanaka@example.com', created_at: '2025-...' }]
PostgreSQLのSQLがそのまま動く。プレースホルダーも$1形式で普通に使える。
トランザクション
await db.transaction(async (tx) => {
await tx.exec(`INSERT INTO users (name, email) VALUES ('佐藤花子', 'sato@example.com')`)
await tx.exec(`UPDATE users SET name = '佐藤はな子' WHERE email = 'sato@example.com'`)
// エラーが発生すると自動ロールバック
})
トランザクションも普通に使える。ACIDが保証されるのは安心感がある。
型安全なクエリ(TypeScript)
interface User {
id: number
name: string
email: string
created_at: Date
}
const result = await db.query<User>(`SELECT * FROM users`)
// result.rows は User[] 型
ジェネリクスで型を指定できる。TypeScriptとの相性も良い。
実践的なユースケース
オフラインファーストなTodoアプリ
import { PGlite } from '@electric-sql/pglite'
class TodoStore {
private db: PGlite
async init() {
this.db = new PGlite('idb://todos')
await this.db.exec(`
CREATE TABLE IF NOT EXISTS todos (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
completed BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT NOW()
)
`)
}
async addTodo(title: string) {
await this.db.exec(`INSERT INTO todos (title) VALUES ($1)`, [title])
}
async toggleTodo(id: number) {
await this.db.exec(`
UPDATE todos SET completed = NOT completed WHERE id = $1
`, [id])
}
async getTodos() {
const result = await this.db.query(`
SELECT * FROM todos ORDER BY created_at DESC
`)
return result.rows
}
async deleteTodo(id: number) {
await this.db.exec(`DELETE FROM todos WHERE id = $1`, [id])
}
}
ネットワーク接続なしで完全に動作する。PWAと組み合わせればオフライン対応アプリが簡単に作れる。
ローカルでのデータ分析
// CSVデータをインポートして分析
const db = new PGlite()
await db.exec(`
CREATE TABLE sales (
date DATE,
product TEXT,
amount INTEGER,
region TEXT
)
`)
// データをバルクインサート
for (const row of csvData) {
await db.exec(`
INSERT INTO sales (date, product, amount, region)
VALUES ($1, $2, $3, $4)
`, [row.date, row.product, row.amount, row.region])
}
// 集計クエリ
const result = await db.query(`
SELECT
region,
product,
SUM(amount) as total,
AVG(amount) as average
FROM sales
WHERE date >= '2025-01-01'
GROUP BY region, product
ORDER BY total DESC
`)
ブラウザ上でSQLを使ったデータ分析ができる。ちょっとしたデータ処理ならサーバー不要。
Reactとの統合
import { useState, useEffect } from 'react'
import { PGlite } from '@electric-sql/pglite'
const db = new PGlite('idb://app-data')
function usePGlite<T>(query: string, deps: any[] = []) {
const [data, setData] = useState<T[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState<Error | null>(null)
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true)
const result = await db.query<T>(query)
setData(result.rows)
} catch (e) {
setError(e as Error)
} finally {
setLoading(false)
}
}
fetchData()
}, deps)
return { data, loading, error }
}
// 使用例
function UserList() {
const { data: users, loading } = usePGlite<User>(
'SELECT * FROM users ORDER BY created_at DESC'
)
if (loading) return <div>Loading...</div>
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}
カスタムフックを作れば、Reactとの統合も簡単。
pgvectorでベクトル検索
import { PGlite } from '@electric-sql/pglite'
import { vector } from '@electric-sql/pglite/vector'
const db = new PGlite({
extensions: { vector }
})
await db.exec('CREATE EXTENSION IF NOT EXISTS vector')
await db.exec(`
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
content TEXT,
embedding vector(384)
)
`)
// 類似検索
const result = await db.query(`
SELECT content, embedding <-> $1 as distance
FROM documents
ORDER BY distance
LIMIT 5
`, [queryEmbedding])
ブラウザでベクトル検索。RAGアプリのローカル版とか作れそう。
注意点
シングルユーザー/シングル接続
PGliteは設計上、同時接続をサポートしていません。WebAssemblyの制約でプロセスフォークができないため。
複数タブで同じデータベースを開くと問題が起きる可能性があるので、SharedWorkerやService Workerで単一インスタンスを管理するなどの工夫が必要。
初期化に時間がかかる
WASMのロードとデータベースの初期化で、初回起動時に数百ミリ秒かかる。ユーザー体験を考えると、ローディング表示は必須。
大規模データには不向き
数GB級のデータを扱う場合は、素直にサーバーサイドのPostgreSQLを使うべき。PGliteは「ローカルで完結するデータ」向け。
まとめ
PGliteを使ってみて感じたこと:
- 学習コスト: PostgreSQLを知っていればゼロ
- セットアップ: npm installするだけ
- 実用性: オフラインファーストアプリに最適
- パフォーマンス: 小〜中規模データなら十分実用的
- 将来性: pgvectorなど拡張機能の対応が進んでいる
正直なところ、「ブラウザでちゃんとしたDBを使いたい」という需要に対する回答としては、現時点でPGlite一択ですね。
特に「オフラインでも動くアプリを作りたい」「サーバーを立てずにデータ処理したい」「PostgreSQLの知識を活かしたい」という人には最適。IndexedDBの低レベルAPIと格闘する時間を節約できますよ。
まだ試していない人は、簡単なTodoアプリあたりから始めてみてください。ブラウザでPostgreSQLが動く感動を味わえるはずです。