polymongo
v1.3.0
Published
Adaptive Multi-Database Wrapper for Mongoose with automatic connection management
Downloads
333
Maintainers
Readme

PolyMongo
MongoDB wrapper for Mongoose with shared multi-database access, optional per-database pools, lifecycle events, and utility operations.
Installation
npm install polymongoQuick Start
import PolyMongo from "polymongo";
import mongoose from "mongoose";
const userSchema = new mongoose.Schema({
name: String,
email: String,
role: String,
});
const wrapper = PolyMongo.createWrapper({
mongoURI: "mongodb://localhost:27017",
defaultDB: "production",
coldStart: true,
retry: 3000,
debug: {
log: true,
logPath: "./logs/polymongo",
logHandler: async (logMessage) => {
console.log(logMessage);
},
},
});
const User = wrapper.wrapModel(mongoose.model("User", userSchema));
await wrapper.connect();
const admins = await User.find({ role: "admin" });
const analyticsUsers = await User.db("analytics").find();Main Ideas
wrapper.wrapModel(Model)gives you a model that can switch databases with.db("name").wrapper.on("event", handler)centralizes lifecycle listeners.wrapper.connect()andwrapper.disconnect()make startup and shutdown explicit.wrapper.advexposes the underlying Mongoose connections and module when you need lower-level control.PolyMongo.Types.*makes the main public types easier to discover from IntelliSense.dbSpecificandpool.configure()let you give certain databases their own pool settings.
Core API
Create a wrapper
const wrapper = PolyMongo.createWrapper({
mongoURI: "mongodb://localhost:27017",
defaultDB: "main",
maxPoolSize: 10,
minFreeConnections: 0,
idleTimeoutMS: 300000,
coldStart: true,
retry: 3000,
debug: {
log: true,
logPath: "./logs",
logHandler: async (logMessage) => {
console.log(logMessage);
},
},
dbSpecific: [
{
dbName: "analytics",
mongoURI: "mongodb://analytics-cluster:27017",
options: {
maxConnections: 25,
autoClose: true,
ttl: 300000,
coldStart: true,
},
},
],
});Connect and disconnect
const result = await wrapper.connect();
console.log(result.success); // true
console.log(result.alreadyConnected); // true when eager startup already connected
console.log(result.defaultDB); // configured default database
const disconnected = await wrapper.disconnect();
console.log(disconnected.alreadyDisconnected);Behavior notes:
- If
coldStart: false, PolyMongo starts connecting during wrapper creation. - Calling
wrapper.connect()after that does not reconnect unnecessarily. - If the wrapper is already connected,
connect()returns{ success: true, alreadyConnected: true }. - If the wrapper is disconnected,
connect()initializes the connection and waits until it is ready. - If
retryis set, PolyMongo keeps retrying failed connections on that fixed interval in milliseconds. - If
retryis omitted, PolyMongo does not auto-retry after connection failures or disconnects.
Logging config
const wrapper = PolyMongo.createWrapper({
mongoURI: "mongodb://localhost:27017",
debug: {
log: true,
logPath: "./logs/polymongo",
logHandler: async (logMessage) => {
// Forward PolyMongo logs to your own system
await myLogger.write(logMessage);
},
},
});Logging behavior:
debug.logturns PolyMongo debug logging on or off.debug.logPathenables file logging through Winston only when you provide a folder path.debug.logHandlerlets you consume each formatted log line yourself.- If
debug.logistruebut nologPathorlogHandleris provided, PolyMongo emits nothing externally. - Top-level
logPathstill works for backward compatibility, butdebug.logPathis the preferred shape.
Advanced Access
You do not lose raw Mongoose access by using PolyMongo. The wrapper exposes the same internals it uses through wrapper.adv.
const wrapper = PolyMongo.createWrapper({
mongoURI: "mongodb://localhost:27017",
retry: 3000,
});
const mongooseRef = wrapper.adv.mongoose;
const primaryConnection = wrapper.adv.getPrimaryConnection();
const ensuredPrimary = wrapper.adv.getOrCreatePrimaryConnection();
const activeDbConnection = wrapper.adv.getConnection("analytics");
const sharedDbConnection = wrapper.adv.getSharedConnection("analytics");This is useful when:
- you want direct access to
mongoose.Schema, sessions, plugins, or connection methods - you need a raw connection for an edge case PolyMongo does not wrap directly
- you want PolyMongo convenience without giving up old Mongoose workflows
IntelliSense Types
PolyMongo also exposes discoverable type names under PolyMongo.Types.*.
import PolyMongo from "polymongo";
type WrapperOptions = PolyMongo.Types.wrapperOptions;
type ConnectEvent = PolyMongo.Types.connectEvent;
type AdvancedAccess = PolyMongo.Types.advancedAccess;Wrap models
const UserModel = mongoose.model("User", userSchema);
const User = wrapper.wrapModel(UserModel);
await User.create({ name: "Ava", role: "admin" });
await User.db("archive").find();
await User.db("analytics").aggregate([{ $match: { role: "admin" } }]);Events in one place
Use one event method for all lifecycle hooks:
const unsubscribe = wrapper.on("connect", async (event) => {
console.log(event.name); // "connect"
console.log(event.dbName);
console.log(event.state);
console.log(event.timestamp);
});
wrapper.on("disconnect", (event) => {
console.log("Disconnected from", event.dbName);
});
wrapper.on("error", (event) => {
console.error(event.error.message);
});Supported event names:
"connect""disconnect""error""onDbConnect"as an alias of"connect""onDbDisconnect"as an alias of"disconnect"
Event payload fields:
name: emitted event namedbName: mongoose connection database namereadyState: raw mongoose ready state numberstate: readable state labelconnection: mongoose connection instancetimestamp: ISO timestamperror: included only for"error"
Configure pools
wrapper.pool.configure(["analytics"], {
maxConnections: 30,
autoClose: true,
ttl: 300000,
coldStart: false,
});
await wrapper.pool.connect(["reporting"], {
maxConnections: 15,
});Stats
const generalStats = wrapper.stats.general();
const analyticsStats = await wrapper.stats.db("analytics");
const databases = await wrapper.stats.listDatabases();Ping
const defaultPing = await wrapper.ping();
const analyticsPing = await wrapper.ping("analytics");Database actions
const exported = await wrapper.actions.exportDB("production");
await wrapper.actions.importDB("backup", exported);
await wrapper.actions.copyDatabase("production", "staging");
await wrapper.actions.dropDatabase("old_data");Streaming helpers use NDJSON so import can process one record at a time instead of rebuilding one giant JSON document in memory.
const writeStream = wrapper.actions.exportDBStream("analytics");
await wrapper.actions.importDBStream("restored", readStream, {
batchSize: 1000,
stopOnError: false,
});NDJSON stream shape:
{"type":"meta","format":"polymongo.ndjson","version":1,"database":"analytics","exportedAt":"2026-04-15T00:00:00.000Z"}
{"type":"collection","collection":"users"}
{"type":"index","collection":"users","index":{"name":"email_1","key":{"email":1},"unique":true}}
{"type":"document","collection":"users","document":{"_id":"...","email":"[email protected]"}}
{"type":"collectionEnd","collection":"users"}Watch stream cleanup
const changes = User.db("production").watch();
changes.on("change", (change) => {
console.log(change);
});
wrapper.actions.closeDBstream("production");
wrapper.actions.closeAllWatches();TypeScript Notes
PolyMongo ships declaration files for:
PolyMongoOptionsWrappedModel<T>- event names and event payloads used by
wrapper.on(...) connect()anddisconnect()result objectswrapper.pool,wrapper.actions,wrapper.stats, andwrapper.ping()return types- advanced access types exposed through
wrapper.adv PolyMongo.Types.*aliases for easier discovery in editors
Because the public methods include JSDoc, editors should show parameter and return help directly in autocomplete.
Migration Notes
Old hook methods
Old style:
wrapper.onDbConnect((connection) => {});
wrapper.onDbDisconnect((connection) => {});New style:
wrapper.on("connect", (event) => {});
wrapper.on("disconnect", (event) => {});Transactions
The old wrapper.transaction(...) helper has been removed from the wrapper API.
If you need transactions, use Mongoose sessions directly from your own application flow so session ownership stays explicit.
Health Checks
if (wrapper.isConnected()) {
console.log("Database ready");
}
console.log(wrapper.getConnectionState());
console.log(await wrapper.ping());License
MIT © Krishnesh Mishra
