RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

【PGlite:Part2】pg-gatewayを利用したTCP接続

はじめに

こんにちは、エンジニア2年目のTKDSです!
前回はPGliteの概要・使い方・速度実験について記事にしました。
今回はさらに、PGliteへのSQLクライアントからの接続を可能とするpg-gatewayについて紹介し、活用例について示します。

復習:PGlite

PGliteは、PostgreSQLをWebAssembly(WASM)にコンパイルした軽量なデータベースエンジンです。
これにより、ブラウザ、Node.js、Bun、DenoなどでPostgresの機能を利用でき、開発者はローカルやサーバーレス環境でデータベース操作を行うことが可能です。
PGliteは、インメモリデータベースやファイルシステム(Node.jsやBun)、IndexedDB(ブラウザ)での永続化をサポートします。

pg-gateway

TypeScript library that implements the Postgres wire protocol from the server-side. It provides APIs you can hook into to handle authentication requests, queries, and other client messages yourself.

翻訳:サーバーサイドでPostgresのワイヤープロトコルを実装するTypeScriptライブラリです。このライブラリは、認証リクエスト、クエリ、およびその他のクライアントメッセージを自分で処理するためのAPIを提供します。

簡単にいうとこのライブラリは、データベースとやりとりするための仕組みをサーバー側で実現するものです。
これによって、簡単にPGliteへ外部のクライアントから接続できるようにしてくれます。

pg-gatewayとPGliteを起動してSQLクライアントから接続する

まずはpg-gatewayを使えるようにします。

# クローンする
git clone https://github.com/supabase-community/pg-gateway.git
# ビルドの設定があるディレクトリに移動
cd pg-gateway/packages/pg-gateway/
# 必要な依存関係のインストール 
npm install
# ビルド
npm run build
# PGlite+pg-gateway用のディレクトリを作る
# 元いたディレクトリに戻る
cd -
# ディレクトリの作成
mkdir -p pglite-with-gateway/lib
cd pglite-with-gateway/lib
# ビルド成果物のコピー
cp ../../pg-gateway/packages/pg-gateway/dist/index.js ./
cp ../../pg-gateway/packages/pg-gateway/dist/index.mjs ./
cp ../../pg-gateway/packages/pg-gateway/dist/index.d.ts ./
# プロジェクトルートで初期化
npm init

生成されたpackage.jsonの中身を以下のように書き換えてください。

{
  "name": "pglite-with-gateway",
  "version": "1.0.0",
  "description": "A project integrating PGlite with pg-gateway",
  "main": "src/app.js",
  "directories": {
    "lib": "lib"
  },
  "scripts": {
    "start": "node src/app.js"
  },
  "author": "",
  "license": "",
  "dependencies": {
    "pg-gateway": "file:./lib/index.js",
  },
  "type": "module"
}

PGliteのインストール

npm install @electric-sql/pglite
# dotenvのインストール
npm install dotenv
# pg-protocolのインストール
npm install pg-protocol

準備が整ったので、コードを準備して起動していきます。
pg-gatewayのREADMEを参考にコードを書きます。

"use strict";
import net from "node:net";
import dotenv from "dotenv";

import { PGlite } from "@electric-sql/pglite";
import { PostgresConnection } from "../lib/index.mjs";

// 環境変数読み込み
dotenv.config();

// PGliteのインスタンス作成
const pg = new PGlite();

// 平文パスワードでの認証処理
async function validateUserCredentials(credentials) {
  const { user, password } = credentials;
  const validUser = process.env.DB_USER;
  const validPassword = process.env.DB_PASSWORD;

  // ユーザー名とパスワードが一致するか確認
  return user === validUser && password === validPassword;
}

// クライアントメッセージの処理
async function handleClientMessage(data, isAuthenticated, connection) {
  if (!isAuthenticated) {
    return false;
  }

  try {
    const [[_, responseData]] = await db.execProtocol(data);
    connection.sendData(responseData);
  } catch (err) {
    console.error("Error processing message:", err);
    connection.sendError(err);
    connection.sendReadyForQuery();
  }
  return true;
}

// クライアント接続処理
function handleClientConnection(socket) {
  const connection = new PostgresConnection(socket, {
    serverVersion: "16.3 (PGlite 0.2.0)",
    authMode: "cleartextPassword", // 平文パスワード認証
    validateCredentials: validateUserCredentials,
    onStartup: async () => {
      await db.waitReady;
      return false;
    },
    onMessage: (data, { isAuthenticated }) =>
      handleClientMessage(data, isAuthenticated, connection),
  });

  socket.on("end", () => {
    console.log("Client disconnected");
  });

  socket.on("error", (err) => {
    console.error("Socket error:", err);
  });
}

// サーバーの起動
function startServer() {
  const server = net.createServer(handleClientConnection);

  server.listen(5432, () => {
    console.log("Server listening on port 5432");
  });

  server.on("error", (err) => {
    console.error("Server error:", err);
  });
}

// サーバーを開始
startServer();

pg-protocol関連でインポートできないエラーがでる場合があります。
エラーメッセージを確認し、node_modules内のファイルを書き換えてください。
作業が完了したら起動します。

npm start

別のターミナルを開いて接続します。

# psqlがない場合はインストールする
sudo apt install -y postgresql-client
# 接続
psql -h localhost -U postgres

以下のように繋がります。

これで、SQLクライアントからPGliteにアクセスすることが可能になりました!

まとめ

pg-gatewayを動かすまでについて解説しました。
情報が少なく、動かせるようになるまで苦労しました。
現時点で実用するには少し厳しいかなと感じました。これからに期待ですね!
(実はpg-gatewayを使ってGoからクエリ実行しようとしてできず、諦めました...。ログインができるのですがpg-gateway側で送信されたクエリを実行できず?)
ここまで読んでいただきありがとうございました!

Copyright © RAKUS Co., Ltd. All rights reserved.