pg(node-postgres)の使い方 — Node.jsからPostgreSQLに接続する定番クライアント
一言でいうと
pg は、Node.js から PostgreSQL に接続するための最も広く使われているクライアントライブラリです。Pure JavaScript 実装とオプションのネイティブバインディング(libpq)を同一APIで提供し、コネクションプーリングやパラメータ化クエリなど、本番運用に必要な機能を備えています。
どんな時に使う?
- Node.js / Express / Fastify などのバックエンドから PostgreSQL にクエリを発行したいとき。ORMを使わず、SQLを直接書きたい場面に最適です。
- コネクションプールを管理して、高負荷な本番環境で安定した DB 接続を維持したいとき。
- LISTEN/NOTIFY によるリアルタイム通知や、COPY コマンドによるバルクインポート/エクスポートなど、PostgreSQL 固有の機能を活用したいとき。
インストール
# npm
npm install pg
# yarn
yarn add pg
# pnpm
pnpm add pg
TypeScript を使う場合は型定義も追加します。
npm install --save-dev @types/pg
pg の基本的な使い方
最も一般的なパターンは、Pool を使ったコネクションプーリングです。
import { Pool } from "pg";
const pool = new Pool({
host: "localhost",
port: 5432,
database: "mydb",
user: "myuser",
password: "mypassword",
max: 20, // プール内の最大接続数
});
// クエリを実行
async function getUsers() {
const result = await pool.query("SELECT id, name, email FROM users WHERE active = $1", [true]);
console.log(result.rows); // [{ id: 1, name: 'Alice', email: 'alice@example.com' }, ...]
return result.rows;
}
getUsers();
ポイント:
Poolはアプリケーション全体で1つだけ作成し、使い回すのが基本です。リクエストごとにnew Pool()を呼んではいけません。
よく使うAPI
1. Pool.query() — プールから直接クエリを実行
最もシンプルな方法です。内部的にコネクションの取得・返却を自動で行います。
import { Pool } from "pg";
const pool = new Pool({ connectionString: "postgresql://myuser:mypassword@localhost:5432/mydb" });
// パラメータ化クエリ(SQLインジェクション対策)
const res = await pool.query(
"SELECT * FROM products WHERE price > $1 AND category = $2",
[1000, "electronics"]
);
console.log(res.rows); // 結果の行配列
console.log(res.rowCount); // 影響を受けた行数
console.log(res.fields); // カラムのメタ情報
2. Pool.connect() — コネクションを手動で取得(トランザクション向け)
トランザクションなど、1つのコネクションで複数のクエリを実行する場合に使います。
const client = await pool.connect();
try {
await client.query("BEGIN");
await client.query(
"INSERT INTO orders (user_id, total) VALUES ($1, $2) RETURNING id",
[42, 9800]
);
await client.query(
"UPDATE inventory SET stock = stock - $1 WHERE product_id = $2",
[1, 101]
);
await client.query("COMMIT");
} catch (err) {
await client.query("ROLLBACK");
throw err;
} finally {
// 必ずコネクションをプールに返却する
client.release();
}
⚠️ 注意:
client.release()を呼び忘れるとコネクションリークが発生し、プールが枯渇します。必ずfinallyブロックで呼んでください。
3. Client — プールを使わない単発接続
スクリプトやマイグレーションなど、短命なプロセスで使います。
import { Client } from "pg";
const client = new Client({
connectionString: "postgresql://myuser:mypassword@localhost:5432/mydb",
});
await client.connect();
try {
const res = await client.query("SELECT NOW() AS current_time");
console.log(res.rows[0].current_time); // 2025-01-15T12:34:56.789Z
} finally {
await client.end();
}
4. LISTEN / NOTIFY — PostgreSQL のリアルタイム通知
PostgreSQL の Pub/Sub 機能をそのまま Node.js で利用できます。
import { Client } from "pg";
const client = new Client({ connectionString: "postgresql://localhost:5432/mydb" });
await client.connect();
// 通知をリッスン
client.on("notification", (msg) => {
console.log("Channel:", msg.channel); // "events"
console.log("Payload:", msg.payload); // "{"type":"order_created","id":123}"
});
await client.query("LISTEN events");
// 別のセッションから NOTIFY events, '{"type":"order_created","id":123}' を実行すると通知が届く
5. カスタム型パーサー — データ型の変換をカスタマイズ
PostgreSQL の型を JavaScript でどう受け取るかを制御できます。
import { Pool, types } from "pg";
// PostgreSQL の NUMERIC 型(OID: 1700)を文字列のまま受け取る代わりに float に変換
types.setTypeParser(1700, (val: string) => parseFloat(val));
// PostgreSQL の BIGINT 型(OID: 20)を number に変換(デフォルトは string)
// ※ Number.MAX_SAFE_INTEGER を超える値がある場合は注意
types.setTypeParser(20, (val: string) => parseInt(val, 10));
const pool = new Pool({ connectionString: "postgresql://localhost:5432/mydb" });
const res = await pool.query("SELECT 123456789.99::numeric AS price, 9007199254740992::bigint AS big");
console.log(typeof res.rows[0].price); // "number"(カスタマイズ後)
類似パッケージとの比較
| 特徴 | pg | postgres(postgres.js) | knex | Prisma |
|---|---|---|---|---|
| レイヤー | ローレベルドライバ | ローレベルドライバ | クエリビルダ | ORM |
| SQL を直接書く | ✅ | ✅ | △(ビルダ経由) | △(一部 rawSQL) |
| コネクションプール | ✅ 内蔵 | ✅ 内蔵 | ✅ 内蔵 | ✅ 内蔵 |
| TypeScript サポート | @types/pg で対応 | ネイティブ対応 | @types/knex | ネイティブ対応 |
| パフォーマンス | 良好 | 非常に高速 | pg に依存 | やや重い |
| LISTEN/NOTIFY | ✅ | ✅ | ❌ | ❌ |
| COPY | ✅(pg-copy-streams) | ✅ | ❌ | ❌ |
| エコシステム・実績 | 非常に大きい(10年以上) | 成長中 | 大きい | 大きい |
| 週間DL数 | 約800万+ | 約100万+ | 約200万+ | 約300万+ |
選定の目安: SQL を直接書きたい+枯れた安定性を求めるなら
pg。パフォーマンス重視ならpostgres.js。クエリビルダが欲しいならknex(内部でpgを使用)。型安全な ORM が欲しいならPrisma。
注意点・Tips
1. 環境変数による接続設定
pg は PostgreSQL 標準の環境変数を自動的に読み取ります。コンストラクタに何も渡さなくても動作します。
export PGHOST=localhost
export PGPORT=5432
export PGDATABASE=mydb
export PGUSER=myuser
export PGPASSWORD=mypassword
// 環境変数が設定されていれば引数不要
const pool = new Pool();
2. BIGINT はデフォルトで文字列になる
PostgreSQL の BIGINT(int8)は JavaScript の Number.MAX_SAFE_INTEGER を超える可能性があるため、デフォルトでは 文字列 として返されます。数値として扱いたい場合は前述の types.setTypeParser() を使いますが、精度の問題に注意してください。
3. SSL 接続
本番環境では SSL 接続が必要なケースが多いです。
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: {
rejectUnauthorized: false, // 自己署名証明書の場合(本番では true 推奨)
},
});
4. プールのエラーハンドリング
アイドル状態のクライアントでエラーが発生した場合、プールの error イベントで捕捉しないとプロセスがクラッシュします。
pool.on("error", (err) => {
console.error("Unexpected error on idle client:", err);
// ここでプロセスを終了するかどうかはアプリケーションの設計次第
});
5. プールの終了
アプリケーションのシャットダウン時には pool.end() を呼んで、すべてのコネクションを適切にクローズしましょう。
process.on("SIGTERM", async () => {
await pool.end();
process.exit(0);
});
6. クエリのタイムアウト
長時間実行されるクエリを防ぐために、statement_timeout を設定できます。
const pool = new Pool({
connectionString: "postgresql://localhost:5432/mydb",
statement_timeout: 30000, // 30秒でタイムアウト
query_timeout: 30000, // クエリ全体のタイムアウト
});
まとめ
pg は Node.js から PostgreSQL を操作するためのデファクトスタンダードなドライバです。コネクションプーリング、パラメータ化クエリ、LISTEN/NOTIFY など、PostgreSQL の機能を薄い抽象化で直接扱えるため、SQL を自分で書きたいエンジニアにとって最も信頼できる選択肢と言えます。10年以上の実績と巨大なエコシステムに支えられており、本番環境での採用実績も豊富です。