@notross/mongo-singleton
Version:
A lightweight, zero-fuss way to get a single shared MongoDB connection across your Node.js codebase.
180 lines • 6.1 kB
JavaScript
import * as mongodb from 'mongodb';
import { logger } from '@notross/node-client-logger';
import { defaultConfig } from './config';
import { buildConnectionString } from './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. 💔
*/
export 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 = 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 ||
buildConnectionString(connection);
}
if (config) {
this.setConfig(config);
}
this.initializeClient();
}
setConfig(config = defaultConfig) {
this.config = config;
}
initializeLogging(props) {
var _a, _b;
const logging = typeof props === 'object'
? (_a = props.logging) !== null && _a !== void 0 ? _a : true
: true;
logger.toggleLogging(logging);
const levels = typeof props === 'object'
? (_b = props.logLevels) !== null && _b !== void 0 ? _b : undefined
: undefined;
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';
logger.log(this.status);
});
client.on('close', () => {
this.status = 'MongoDB connection closed';
logger.log(this.status);
});
client.on('error', (err) => {
this.status = 'MongoDB connection error';
this.error = err;
logger.error(this.status, err);
});
client.on('reconnect', () => {
this.status = 'MongoDB reconnected';
logger.log(this.status);
});
client.on('reconnectFailed', () => {
this.status = 'MongoDB reconnection failed';
logger.error(this.status);
});
client.on('timeout', () => {
this.status = 'MongoDB connection timed out';
logger.error(this.status);
});
client.on('serverHeartbeatFailed', (err) => {
this.status = 'MongoDB server heartbeat failed:';
this.error = err;
logger.error(this.status, this.error);
});
client.on('serverHeartbeatSucceeded', () => {
this.status = 'MongoDB server heartbeat succeeded';
logger.log(this.status);
});
client.on('serverClosed', () => {
this.status = 'MongoDB server closed';
logger.log(this.status);
});
client.on('serverOpening', () => {
this.status = 'MongoDB server opening';
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';
logger.log(this.status);
}).catch((err) => {
this.status = 'Error disconnecting from MongoDB';
this.error = err;
logger.error(this.status, err);
});
}
else {
this.status = 'No MongoDB client to disconnect';
logger.warn(this.status);
}
}
async getDb() {
const { database } = await this.connect();
return database;
}
getCollection(name) {
const database = this._getDb();
return database.collection(name);
}
}
//# sourceMappingURL=mongo-singleton.js.map