@accounter/server
Version:
Accounter GraphQL server
104 lines • 3.55 kB
JavaScript
import { __decorate, __metadata } from "tslib";
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Injectable, Scope } from 'graphql-modules';
import { Pool } from 'pg';
import 'reflect-metadata';
/**
* DBProvider - Singleton connection pool manager for PostgreSQL database access.
*
* This provider manages the PostgreSQL connection pool and provides two access patterns:
*
* 1. **System-level access** (migrations, background jobs):
* - Use the `pool` property directly for operations that bypass Row-Level Security (RLS)
* - Example: `dbProvider.pool.query('SELECT * FROM table')`
*
* 2. **Request-level access** (GraphQL operations):
* - MUST use TenantAwareDBClient wrapper (next phase)
* - Enforces RLS by setting transaction-scoped variables
* - Never access `pool` directly from resolvers
*
* @example System-level usage (migrations)
* ```typescript
* const dbProvider = injector.get(DBProvider);
* await dbProvider.pool.query('CREATE TABLE ...');
* ```
*
* @example Request-level usage (GraphQL resolvers)
* ```typescript
* // DO NOT use DBProvider directly in resolvers!
* // Use TenantAwareDBClient instead:
* const db = context.db; // TenantAwareDBClient instance
* await db.query('SELECT * FROM charges'); // Automatically filtered by RLS
* ```
*/
let DBProvider = class DBProvider {
pool;
/**
* PostgreSQL connection pool.
*
* **IMPORTANT**: Direct pool access should only be used for:
* - Database migrations
* - Background jobs that operate across all tenants
* - System-level administrative operations
*
* GraphQL resolvers MUST use TenantAwareDBClient to ensure Row-Level Security.
*/
constructor(pool) {
this.pool = pool;
if (!pool) {
throw new Error('DBProvider: Pool instance is required');
}
}
/**
* Execute a query against the database.
*
* **WARNING**: This method bypasses Row-Level Security (RLS).
* Only use for system-level operations. GraphQL resolvers should use TenantAwareDBClient.
*
* @param queryStatement - SQL query string
* @param values - Query parameters (optional)
* @returns Query result with typed rows
* @throws Error if database connection not initialized
*/
async query(queryStatement, values) {
if (!this.pool) {
throw new Error('DB connection not initialized');
}
const result = await this.pool.query(queryStatement, values);
return { ...result, rowCount: result.rowCount ?? 0 };
}
/**
* Check database connection health.
*
* Executes a simple `SELECT 1` query to verify the database is accessible.
* Useful for readiness probes and monitoring.
*
* @returns Promise<boolean> - true if database is healthy, false otherwise
*/
async healthCheck() {
try {
const result = await this.pool.query('SELECT 1 as health');
return result.rows[0]?.health === 1;
}
catch {
return false;
}
}
/**
* Gracefully shut down the database connection pool.
* Should be called during application termination (SIGTERM/SIGINT).
*/
async shutdown() {
if (this.pool) {
await this.pool.end();
}
}
};
DBProvider = __decorate([
Injectable({
scope: Scope.Singleton,
}),
__metadata("design:paramtypes", [Pool])
], DBProvider);
export { DBProvider };
//# sourceMappingURL=db.provider.js.map