npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

polymongo

v1.3.0

Published

Adaptive Multi-Database Wrapper for Mongoose with automatic connection management

Downloads

333

Readme

Project Banner

PolyMongo

MongoDB wrapper for Mongoose with shared multi-database access, optional per-database pools, lifecycle events, and utility operations.

npm version License: MIT TypeScript

Installation

npm install polymongo

Quick 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() and wrapper.disconnect() make startup and shutdown explicit.
  • wrapper.adv exposes the underlying Mongoose connections and module when you need lower-level control.
  • PolyMongo.Types.* makes the main public types easier to discover from IntelliSense.
  • dbSpecific and pool.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 retry is set, PolyMongo keeps retrying failed connections on that fixed interval in milliseconds.
  • If retry is 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.log turns PolyMongo debug logging on or off.
  • debug.logPath enables file logging through Winston only when you provide a folder path.
  • debug.logHandler lets you consume each formatted log line yourself.
  • If debug.log is true but no logPath or logHandler is provided, PolyMongo emits nothing externally.
  • Top-level logPath still works for backward compatibility, but debug.logPath is 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 name
  • dbName: mongoose connection database name
  • readyState: raw mongoose ready state number
  • state: readable state label
  • connection: mongoose connection instance
  • timestamp: ISO timestamp
  • error: 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:

  • PolyMongoOptions
  • WrappedModel<T>
  • event names and event payloads used by wrapper.on(...)
  • connect() and disconnect() result objects
  • wrapper.pool, wrapper.actions, wrapper.stats, and wrapper.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