@notross/mongo-singleton
Version:
A lightweight, zero-fuss way to get a single shared MongoDB connection across your Node.js codebase.
217 lines • 7.96 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.MongoSingleton = void 0;
const mongodb = __importStar(require("mongodb"));
const node_client_logger_1 = require("@notross/node-client-logger");
const config_1 = require("./config");
const utils_1 = require("./utils");
/**
* MongoSingleton
*
* Manages a single, shared MongoDB connection for the application.
* Subsequent calls to `.connect()` return the same connection.
*
* Like me, it's single and looking for a connection. 💔
*/
class MongoSingleton {
/**
* @param connection - Either a full ConnectionProps object,
* a SparseConnectionProps object, or a
* raw MongoDB URI string.
* @param database - The name of the database to operate on.
*/
constructor(props) {
this.config = config_1.defaultConfig;
this.databaseName = '';
this.uri = 'mongodb://localhost:27017';
this.client = null;
this.database = null;
this.status = 'Disconnected';
this.error = null;
if (props) {
this.setup(props);
}
this.setConfig(props === null || props === void 0 ? void 0 : props.config);
this.collection = this.getCollection.bind(this);
this.configure = this.setConfig.bind(this);
this.connectedDb = this.getDb.bind(this);
this.db = this._getDb.bind(this);
this.init = this.setup.bind(this);
}
setup({ config, connection, database } = {
connection: 'mongodb://localhost:27017',
database: '',
}) {
this.initializeLogging(connection);
this.databaseName = database;
if (typeof connection === 'string') {
this.uri = connection;
}
else {
this.uri = connection.uri ||
(0, utils_1.buildConnectionString)(connection);
}
if (config) {
this.setConfig(config);
}
this.initializeClient();
}
setConfig(config = config_1.defaultConfig) {
this.config = config;
}
initializeLogging(props) {
var _a, _b;
const logging = typeof props === 'object'
? (_a = props.logging) !== null && _a !== void 0 ? _a : true
: true;
node_client_logger_1.logger.toggleLogging(logging);
const levels = typeof props === 'object'
? (_b = props.logLevels) !== null && _b !== void 0 ? _b : undefined
: undefined;
node_client_logger_1.logger.setLevels(levels);
}
initializeClient() {
if (this.client) {
return this.client;
}
this.client = new mongodb.MongoClient(this.uri, this.config);
return this.client;
}
initializeDatabase() {
const client = this.client;
this.database = client.db(this.databaseName);
return this.database;
}
_getDb() {
return this.database || this.initializeDatabase();
}
/**
* Establishes a connection to MongoDB if not already connected.
* If already connected, returns the existing client and database.
*
* @returns Promise resolving to the connected MongoClient and Db.
* @example
* const { client, database } = await mongoClient.connect();
*/
async connect() {
if (this.client) {
return {
client: this.client,
database: this._getDb(),
};
}
const client = this.initializeClient();
await client.connect().then(() => this.initializeDatabase());
client.on('connectionReady', () => {
this.status = 'MongoDB connection is ready';
node_client_logger_1.logger.log(this.status);
});
client.on('close', () => {
this.status = 'MongoDB connection closed';
node_client_logger_1.logger.log(this.status);
});
client.on('error', (err) => {
this.status = 'MongoDB connection error';
this.error = err;
node_client_logger_1.logger.error(this.status, err);
});
client.on('reconnect', () => {
this.status = 'MongoDB reconnected';
node_client_logger_1.logger.log(this.status);
});
client.on('reconnectFailed', () => {
this.status = 'MongoDB reconnection failed';
node_client_logger_1.logger.error(this.status);
});
client.on('timeout', () => {
this.status = 'MongoDB connection timed out';
node_client_logger_1.logger.error(this.status);
});
client.on('serverHeartbeatFailed', (err) => {
this.status = 'MongoDB server heartbeat failed:';
this.error = err;
node_client_logger_1.logger.error(this.status, this.error);
});
client.on('serverHeartbeatSucceeded', () => {
this.status = 'MongoDB server heartbeat succeeded';
node_client_logger_1.logger.log(this.status);
});
client.on('serverClosed', () => {
this.status = 'MongoDB server closed';
node_client_logger_1.logger.log(this.status);
});
client.on('serverOpening', () => {
this.status = 'MongoDB server opening';
node_client_logger_1.logger.log(this.status);
});
return {
client: client,
database: this._getDb(),
};
}
/**
* Gracefully closes the MongoDB connection and resets internal state.
* If no client exists, logs a warning instead.
*/
async disconnect() {
if (this.client) {
await this.client.close().then(() => {
this.client = null;
this.database = null;
this.status = 'Disconnected from MongoDB';
node_client_logger_1.logger.log(this.status);
}).catch((err) => {
this.status = 'Error disconnecting from MongoDB';
this.error = err;
node_client_logger_1.logger.error(this.status, err);
});
}
else {
this.status = 'No MongoDB client to disconnect';
node_client_logger_1.logger.warn(this.status);
}
}
async getDb() {
const { database } = await this.connect();
return database;
}
getCollection(name) {
const database = this._getDb();
return database.collection(name);
}
}
exports.MongoSingleton = MongoSingleton;
//# sourceMappingURL=mongo-singleton.js.map