PostgreSQL connection pool manager adapter for @storehouse/core. Provides seamless integration with PostgreSQL using the official node-postgres driver.
npm install @storehouse/core pg @storehouse/pg
index.ts
import { Storehouse } from '@storehouse/core';
import { PgManager } from '@storehouse/pg';
// Register the manager
Storehouse.add({
postgres: {
type: PgManager,
config: {
host: 'localhost',
port: 5432,
database: 'mydb',
user: 'postgres',
password: 'password',
max: 20,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000
}
}
});
import { Storehouse } from '@storehouse/core';
import { PgManager } from '@storehouse/pg';
// Get the manager
const manager = Storehouse.getManager<PgManager>('postgres');
if (manager) {
// Query directly using the pool
const result = await manager.query('SELECT * FROM users WHERE id = $1', [1]);
console.log('User:', result.rows[0]);
// Or acquire a client for multiple queries
const client = await manager.getConnection();
try {
await client.query('BEGIN');
await client.query('INSERT INTO users (name, email) VALUES ($1, $2)', ['John', 'john@example.com']);
await client.query('INSERT INTO audit_log (action) VALUES ($1)', ['user_created']);
await client.query('COMMIT');
} catch (error) {
await client.query('ROLLBACK');
throw error;
} finally {
client.release();
}
}
The package provides helper functions that throw errors instead of returning undefined, making your code cleaner and safer.
getManager()Retrieves a PgManager instance from the registry.
import { Storehouse } from '@storehouse/core';
import { getManager } from '@storehouse/pg';
const manager = getManager(Storehouse, 'postgres');
const result = await manager.query('SELECT NOW()');
Throws:
ManagerNotFoundError - If the manager doesn't existInvalidManagerConfigError - If the manager is not a PgManager instancegetConnection()Retrieves a PostgreSQL client from the connection pool.
import { Storehouse } from '@storehouse/core';
import { getConnection } from '@storehouse/pg';
const client = await getConnection(Storehouse, 'postgres');
try {
const result = await client.query('SELECT * FROM users');
console.log(result.rows);
} finally {
client.release();
}
Throws:
ManagerNotFoundError - If the manager doesn't existThe PgManager extends the pg Pool class with additional Storehouse integration features.
query(queryText, values?): Promise<QueryResult>Executes a query using the pool. The pool will handle client acquisition and release automatically.
const result = await manager.query('SELECT * FROM users WHERE age > $1', [18]);
console.log(result.rows);
connect(): Promise<PoolClient>Acquires a client from the connection pool for multiple operations.
const client = await manager.connect();
try {
const result = await client.query('SELECT NOW()');
console.log(result.rows[0]);
} finally {
client.release();
}
getConnection(): Promise<PoolClient>Alias for connect(). Acquires a client from the connection pool.
const client = await manager.getConnection();
try {
await client.query('SELECT 1');
} finally {
client.release();
}
releaseAll(err?: Error | boolean): Promise<void>Releases all currently checked-out clients back to the pool. Useful for graceful shutdown scenarios.
// Release all clients normally
await manager.releaseAll();
// Release all clients with an error
await manager.releaseAll(new Error('Forced release'));
closeConnection(err?: Error | boolean): Promise<void>Releases all clients and closes the connection pool gracefully.
// Graceful close
await manager.closeConnection();
// Close with error
await manager.closeConnection(new Error('Forced shutdown'));
isConnected(): Promise<boolean>Checks if the connection pool is active and has clients available.
const connected = await manager.isConnected();
if (connected) {
console.log('PostgreSQL pool is active');
}
healthCheck(): Promise<PgHealthCheckResult>Performs a comprehensive health check including query test and pool metrics.
const health = await manager.healthCheck();
if (health.healthy) {
console.log(`✓ PostgreSQL is healthy`);
console.log(` Latency: ${health.details.latency}`);
console.log(` Pool: ${health.details.idleCount}/${health.details.totalCount} idle`);
} else {
console.error(`✗ PostgreSQL is unhealthy: ${health.message}`);
}
end(): Promise<void>Shuts down the pool and destroys all clients.
await manager.end();
The health check returns a detailed result object:
healthy: boolean - Overall health statusmessage: string - Descriptive message about the health statustimestamp: number - Timestamp when the health check was performedlatency?: number - Response time in millisecondsdetails: object - Detailed pool information
name: string - Manager nametotalCount?: number - Total number of clients in the poolidleCount?: number - Number of idle clientswaitingCount?: number - Number of clients waiting for a connectionlatency?: string - Response time in msended?: boolean - Whether the pool has endederror?: string - Error details (if unhealthy)You can register multiple PostgreSQL connections:
import { Storehouse } from '@storehouse/core';
import { PgManager, getManager } from '@storehouse/pg';
Storehouse.add({
primary: {
type: PgManager,
config: {
host: 'localhost',
database: 'maindb',
user: 'postgres',
password: 'password'
}
},
analytics: {
type: PgManager,
config: {
host: 'analytics.example.com',
database: 'analyticsdb',
user: 'readonly',
password: 'password'
}
}
});
// Access specific managers
const primaryManager = getManager(Storehouse, 'primary');
const analyticsManager = getManager(Storehouse, 'analytics');
Set the manager type to simplify configuration and use string identifiers instead of class references:
import { Storehouse } from '@storehouse/core';
import { PgManager } from '@storehouse/pg';
// Set default manager type
Storehouse.setManagerType(PgManager);
// Now you can use type string instead of class
Storehouse.add({
postgres: {
type: '@storehouse/pg',
config: {
host: 'localhost',
database: 'mydb',
user: 'postgres',
password: 'password'
}
}
});
If you need queries to target a specific schema by default, set the search_path when clients connect:
import { Storehouse } from '@storehouse/core';
import { getManager } from '@storehouse/pg';
const manager = getManager(Storehouse, 'postgres');
manager.on('connect', client => {
// Set the search_path for each new client to 'myschema'
client.query('SET search_path TO myschema');
});
// Now queries will use 'myschema' by default
const result = await manager.query('SELECT * FROM movies LIMIT 100');
console.log(result.rows);
Use client checkout for transaction control:
import { Storehouse } from '@storehouse/core';
import { getConnection } from '@storehouse/pg';
const client = await getConnection(Storehouse, 'postgres');
try {
await client.query('BEGIN');
await client.query('UPDATE accounts SET balance = balance - $1 WHERE id = $2', [100, 1]);
await client.query('UPDATE accounts SET balance = balance + $1 WHERE id = $2', [100, 2]);
await client.query('COMMIT');
console.log('Transaction completed successfully');
} catch (error) {
await client.query('ROLLBACK');
console.error('Transaction failed:', error);
throw error;
} finally {
client.release();
}
The manager automatically logs connection pool lifecycle events. These are logged using the @novice1/logger package and can be enabled with Debug mode:
import { Debug } from '@novice1/logger';
Debug.enable('@storehouse/pg*');
Events logged:
acquire - Client acquired from poolconnect - New client connected to databaseerror - Connection or pool errorsrelease - Client released back to poolremove - Client removed from poolThe package is written in TypeScript and provides full type definitions for type-safe operations:
import { Storehouse } from '@storehouse/core';
import { PoolClient, QueryResult } from 'pg';
import { PgManager, getManager, getConnection } from '@storehouse/pg';
// Typed manager
const manager = getManager<PgManager>(Storehouse, 'postgres');
// Typed connection
const client: PoolClient = await getConnection(Storehouse, 'postgres');
// Type-safe query results
interface User {
id: number;
name: string;
email: string;
}
const result = await manager.query<User>('SELECT * FROM users');
// result.rows is typed as User[]
const users: User[] = result.rows;
users.forEach(user => {
console.log(user.name); // Fully typed
});
You can extend PoolClient with custom properties:
import { PoolClient } from 'pg';
import { PgManager } from '@storehouse/pg';
interface MyPoolClient extends PoolClient {
database?: string;
}
const manager = Storehouse.getManager<PgManager<MyPoolClient>>('postgres');
const client = await manager.getConnection();
console.log('Database:', client.database);
All helper functions throw specific errors for better error handling:
import { Storehouse } from '@storehouse/core';
import { getManager, getConnection } from '@storehouse/pg';
import {
ManagerNotFoundError,
InvalidManagerConfigError
} from '@storehouse/core';
try {
const manager = getManager(Storehouse, 'nonexistent');
} catch (error) {
if (error instanceof ManagerNotFoundError) {
console.error('Manager not found:', error.message);
} else if (error instanceof InvalidManagerConfigError) {
console.error('Invalid manager type:', error.message);
}
}
try {
const client = await getConnection(Storehouse, 'nonexistent');
} catch (error) {
if (error instanceof ManagerNotFoundError) {
console.error('Manager not found:', error.message);
}
}
getConnection() to acquire clients for transaction blocksmax and min pool sizes based on your workloadcloseConnection() when shutting down your applicationtotalCount, idleCount, and waitingCount regularlyMIT