PostgreSQL Connection Pool
pg-pool is a specialized connection pooling library for Node.js applications using `node-postgres` to interact with PostgreSQL databases. Currently stable at version 3.13.0, it is actively maintained as part of the `node-postgres` family, with releases typically aligning with updates to the core `pg` client. This library is designed to improve application performance and reliability by managing a set of reusable database connections, thus reducing the overhead of repeatedly establishing and tearing down connections. Key features include configurable connection limits (`max`), idle timeouts (`idleTimeoutMillis`), connection establishment timeouts (`connectionTimeoutMillis`), and the ability to replace connections after a certain number of uses (`maxUses`). A crucial differentiator from some other database clients is that `pg-pool` does not directly accept a database URL string; instead, developers must parse the URL into a configuration object before passing it to the Pool constructor. It offers a modern, promise-based API that integrates smoothly with `async/await` patterns, simplifying resource management (client acquisition and release) compared to manual connection handling. It also supports pooling both the standard `pg.Client` and `pg.native.Client` instances.
Common errors
-
Error: connect ECONNREFUSED ::1:5432
cause The application could not establish a connection to the PostgreSQL database. This usually indicates the database server is not running, is not accessible from the application's host, or has incorrect firewall rules.fixVerify that the PostgreSQL server is running and listening on the specified host and port (default 5432). Check firewall rules and ensure the database user has network access. -
Error: Client was not released to the pool
cause A client was acquired from the pool using `pool.connect()` but was not returned via `client.release()`, leading to resource exhaustion.fixEnsure every `pool.connect()` call is paired with a `client.release()` call, typically within a `try...finally` block to guarantee release even if errors occur. -
TypeError: Pool is not a constructor
cause The `Pool` constructor was imported incorrectly, most commonly by trying to destructure a CommonJS default export (e.g., `const { Pool } = require('pg-pool')`) or by using a named import for a CommonJS default export in an ESM context (e.g., `import { Pool } from 'pg-pool'`).fixFor CommonJS, use `const Pool = require('pg-pool')`. For ESM, use `import Pool from 'pg-pool'`. -
Error: parameter "user" must be a string
cause A required configuration property (like `user`, `host`, `database`, `password`) for the `Pool` constructor is missing or has an incorrect type, often due to faulty URL parsing.fixReview your `pg-pool` configuration object. Ensure all mandatory properties are present and their values are of the correct type (e.g., strings for `user`, `password`, `host`, `database`, number for `port`). Double-check your URL parsing logic if using `process.env.DATABASE_URL`.
Warnings
- gotcha The `Pool` constructor does not accept a database URL string directly. Developers must manually parse the URL (e.g., using Node.js's built-in `URL` module) into a configuration object before passing it to the Pool constructor.
- gotcha The `maxUses` configuration option causes a client to be disconnected and replaced after it has been used a specified number of times. While useful for preventing resource leaks in long-running applications, it can lead to unexpected connection churn if not understood, potentially impacting performance due to frequent client recreation.
- gotcha `pg-pool` requires `node-postgres` (`pg`) as a peer dependency. If `pg` is not installed or if its version does not meet `pg-pool`'s requirements (e.g., `>=8.0`), `pg-pool` will not function correctly.
- gotcha Failing to release a client back to the pool after use (e.g., by omitting `client.release()` in a `finally` block) will exhaust the connection pool, causing subsequent `pool.connect()` calls to hang indefinitely or time out. This is a common source of application unresponsiveness.
- gotcha While `ssl: true` is commonly used, its default behavior (e.g., `rejectUnauthorized`) can vary. In some environments (like Heroku), `ssl: { rejectUnauthorized: false }` might be necessary, but this reduces security. In production, ensure proper SSL certificate validation.
Install
-
npm install pg-pool -
yarn add pg-pool -
pnpm add pg-pool
Imports
- Pool
const { Pool } = require('pg-pool')const Pool = require('pg-pool') - Pool (ESM)
import { Pool } from 'pg-pool'import Pool from 'pg-pool'
- Pool (TypeScript Type)
import type { Pool } from 'pg-pool'import type Pool from 'pg-pool'
Quickstart
const Pool = require('pg-pool');
const { URL } = require('url'); // Node.js built-in URL parser
// In a real application, ensure process.env.DATABASE_URL is set,
// e.g., DATABASE_URL=postgres://user:password@localhost:5432/mydatabase
const databaseUrl = process.env.DATABASE_URL || 'postgres://testuser:testpass@localhost:5432/testdb';
const params = new URL(databaseUrl);
const auth = params.username && params.password ? params.username.split(':') : [params.username, params.password];
const config = {
user: auth[0],
password: auth[1],
host: params.hostname,
port: parseInt(params.port || '5432'), // Default PostgreSQL port
database: params.pathname.split('/')[1],
ssl: params.protocol === 'https:' || (process.env.NODE_ENV === 'production' && databaseUrl.includes('sslmode=require')), // Use SSL in production or if protocol specifies
max: 10, // Max clients in the pool
idleTimeoutMillis: 30000, // Close idle clients after 30 seconds
connectionTimeoutMillis: 2000, // Return error after 2 seconds if connection not established
};
const pool = new Pool(config);
(async () => {
let client;
try {
// Acquire a client from the pool
client = await pool.connect();
console.log('Client acquired from pool.');
// Execute a query using the client
const res = await client.query('SELECT $1::text as message', ['Hello from pg-pool with async/await!']);
console.log('Query result:', res.rows[0].message);
// Demonstrate direct pool.query (acquires, queries, and releases client automatically)
const timeRes = await pool.query('SELECT NOW() as current_time');
console.log('Current database time via direct query:', timeRes.rows[0].current_time);
} catch (err) {
console.error('Database operation failed:', err.message, err.stack);
} finally {
// Release the client back to the pool to prevent resource leaks
if (client) {
client.release();
console.log('Client released back to pool.');
}
// It's good practice to end the pool when the application is shutting down
// For a quickstart, we end it here, but in a real app it's usually on process exit.
await pool.end();
console.log('Pool has been ended.');
}
})();