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

mongoose-utility-kit

v0.4.0

Published

A set of utility functions for the Mongoose library

Readme

mongoose-utility-kit

A set of utility functions and plugins to simplify common mongoose operations. Designed for TypeScript and mongoose projects with strict type safety.

npm

Features

  • Type-safe: Built with TypeScript for robust type checking.
  • Efficient: Reduces redundant database queries and simplifies data transformations.
  • Flexible: Handles documents, ObjectIds, and string IDs seamlessly.
  • Population Ready: Designed for mongoose documents with populated references.
  • Data Transformation: Provides plugins to convert query results into Map or Record objects.

Installation

mongoose >=8 is required as peer dependency

npm install mongoose-utility-kit

or

yarn add mongoose-utility-kit

TypeScript Usage Notes

These helpers are designed for mongoose projects using TypeScript with proper document typing. For best results:

  1. Define your hydrated document types using mongoose's HydratedDocument:
// user.ts
import { HydratedDocument } from 'mongoose';

export interface IUser {
  name: string;
  email: string;
}

export interface IUserDocumentOverrides { /* ... */ }

export interface IUserVirtuals { /* ... */ }

export type UserHydratedDocument = HydratedDocument<IUser, IUserDocumentOverrides & IUserVirtuals>;
  1. Use PopulatedDoc for population references:
// comment.ts
import { Types, PopulatedDoc } from "mongoose";
import { UserHydratedDocument } from "./user";

export interface IComment {
  user: PopulatedDoc<UserHydratedDocument>;
  text: string;
}

Usage

Utility Functions

isPopulated

Type guard to check if a PopulatedDoc field is a populated document or an ObjectId:

import { isPopulated } from "mongoose-utility-kit";

const comment = await Comment.findById(someId).orFail();

if (isPopulated(comment.user)) {
  // TypeScript knows comment.user is UserHydratedDocument
  console.log(comment.user.name);
} else {
  // comment.user is an ObjectId
  console.log(comment.user.toString());
}

ensurePopulated

Assertion version of isPopulated. Returns the populated document or throws if the field is not populated:

import { ensurePopulated } from "mongoose-utility-kit";

const comment = await Comment.findById(someId).populate("user").orFail();
const user = ensurePopulated(comment.user);
// TypeScript knows user is UserHydratedDocument
console.log(user.name);

// Without population — throws Error
const comment2 = await Comment.findById(someId).orFail();
ensurePopulated(comment2.user); // Error: Expected a populated document

getPopulatedDocumentId

Extract the _id as a string from a field that may or may not be populated:

import { getPopulatedDocumentId } from "mongoose-utility-kit";

// With ObjectId reference
const comment = await Comment.findById(someId).orFail();
getPopulatedDocumentId(comment.user); // '507f191e810c19729de860ea'

// With populated document
const comment = await Comment.findById(someId)
  .populate<{ user: UserHydratedDocument }>("user")
  .orFail();
getPopulatedDocumentId(comment.user); // '507f191e810c19729de860ea'

toObjectId

Convert a string, ObjectId, or Document to a Types.ObjectId:

import { toObjectId } from "mongoose-utility-kit";

// From an ObjectId — returns as-is
toObjectId(someObjectId);

// From a string — constructs a new ObjectId
toObjectId("507f191e810c19729de860ea");

// From a Document — extracts _id
toObjectId(userDocument);

// Throws on invalid string
toObjectId("invalid"); // Error!

areIdsEqual

Compare two values by their underlying MongoDB ID. Accepts strings, ObjectIds, Documents, and populated documents. Returns false for null or undefined:

import { areIdsEqual } from "mongoose-utility-kit";

areIdsEqual(user._id, comment.user);               // true (ObjectId vs PopulatedDoc)
areIdsEqual(user, "507f191e810c19729de860ea");       // true (Document vs string)
areIdsEqual(user._id, otherUser._id);               // false
areIdsEqual(null, user._id);                         // false

findOrReturnInstance

Avoid redundant queries: returns the document if already hydrated, otherwise fetches it by ID. Accepts ObjectIds and string IDs:

import { findOrReturnInstance } from "mongoose-utility-kit";

// With ObjectId — fetches from DB
const user = await findOrReturnInstance(comment.user, UserModel);

// With string ID — fetches from DB
const user = await findOrReturnInstance("507f191e810c19729de860ea", UserModel);

// With populated document — returns it directly, no DB query
const user = await findOrReturnInstance(comment.user, UserModel);

// With custom error
const user = await findOrReturnInstance(
  someId,
  UserModel,
  new Error("User not found"),
);

// With error factory
const user = await findOrReturnInstance(
  someId,
  UserModel,
  () => new Error("User not found"),
);

findOrReturnManyInstances

Batch version of findOrReturnInstance. Separates already-hydrated documents from IDs, performs a single $in query for the missing ones, and returns the combined result. Accepts ObjectIds and string IDs:

import { findOrReturnManyInstances } from "mongoose-utility-kit";

const inputs = [
  existingUserDocument,          // already hydrated — no query needed
  new Types.ObjectId("..."),     // ObjectId — will be fetched
  "507f191e810c19729de860ea",    // string ID — will be fetched
];

const users = await findOrReturnManyInstances(inputs, UserModel);

Mongoose Plugins

These plugins add query helpers that transform results into Map or Record objects, keyed by _id.

  • toMap(): Returns a Map<string, Document>.
  • toRecords(): Returns a Record<string, Document> (plain object).

Integration

  1. Import the plugins and type helpers:

    import {
      toMap, ToMapQueryHelpers,
      toRecords, ToRecordsQueryHelpers,
    } from "mongoose-utility-kit";
  2. Register the plugins with your schema:

    type MyModelHydratedDocument = HydratedDocument<IMyModel>;
    
    // TVirtuals defaults to {}, so it can be omitted when not using virtuals
    type MyModelType = Model<
      IMyModel,
      ToMapQueryHelpers<IMyModel, MyModelHydratedDocument> &
        ToRecordsQueryHelpers<IMyModel, MyModelHydratedDocument>,
      {},
      {},
      MyModelHydratedDocument
    >;
    
    const schema = new mongoose.Schema<IMyModel, MyModelType>({
      name: String,
      value: Number,
    });
    
    schema.plugin(toMap);
    schema.plugin(toRecords);
    
    const MyModel = mongoose.model<IMyModel, MyModelType>("MyModel", schema);

    With virtuals, pass the virtuals type as the third generic parameter:

    ToMapQueryHelpers<IMyModel, MyModelHydratedDocument, IMyModelVirtuals>

Examples

// Converts the query result to a Map
const myMap = await MyModel.find({}).toMap();
// Map<string, MyModelHydratedDocument>

// Converts the query result to a Record
const myRecords = await MyModel.find({}).toRecords();
// Record<string, MyModelHydratedDocument>

// Works with lean queries
const leanMap = await MyModel.find({}).lean().toMap();
// Map<string, IMyModel>

// Works with lean queries + virtuals
const leanMap = await MyModel.find({})
  .lean<(IMyModel & IMyModelVirtuals)[]>({ virtuals: true })
  .toMap();
// Map<string, IMyModel & IMyModelVirtuals>

API Reference

isPopulated

function isPopulated<HydratedDocType extends Document>(
  value: PopulatedDoc<HydratedDocType>
): value is HydratedDocType;

Returns: true if the value is a populated document, false if it's an ObjectId, null, or undefined.

ensurePopulated

function ensurePopulated<HydratedDocType extends Document>(
  value: PopulatedDoc<HydratedDocType>
): HydratedDocType;

Returns: The populated document with its type narrowed. Throws: Error if the value is not a populated document.

getPopulatedDocumentId

function getPopulatedDocumentId<HydratedDocType extends Document>(
  populatedDocument: PopulatedDoc<HydratedDocType>
): string;

Handles: Both populated documents and raw ObjectIds. Throws: Error if the value has no valid _id.

toObjectId

function toObjectId(
  value: string | Types.ObjectId | Document
): Types.ObjectId;

Returns: A Types.ObjectId. Throws: Error if a string value is not a valid 24-character hex ObjectId.

areIdsEqual

function areIdsEqual(
  a: string | Types.ObjectId | Document | PopulatedDoc<any> | null | undefined,
  b: string | Types.ObjectId | Document | PopulatedDoc<any> | null | undefined
): boolean;

Returns: true if both values resolve to the same ID string. Returns false if either value is null or undefined.

findOrReturnInstance

async function findOrReturnInstance<HydratedDocType extends Document>(
  docOrId: HydratedDocType | Types.ObjectId | string,
  MongooseModel: Model<any, any, any, any, HydratedDocType>,
  err?: Error | (() => Error)
): Promise<HydratedDocType>;

Throws: Default error or custom error if document not found.

findOrReturnManyInstances

async function findOrReturnManyInstances<HydratedDocType extends Document>(
  docOrIds: (HydratedDocType | Types.ObjectId | string)[],
  MongooseModel: Model<any, any, any, any, HydratedDocType>
): Promise<HydratedDocType[]>;

Note: Non-existent IDs are silently omitted from the result (consistent with find({ _id: { $in: [...] } }) behavior). String IDs are converted to ObjectIds internally.

ToMapQueryHelpers

interface ToMapQueryHelpers<RawDocType, HydratedDocType, TVirtuals = {}> { ... }

ToRecordsQueryHelpers

interface ToRecordsQueryHelpers<RawDocType, HydratedDocType, TVirtuals = {}> { ... }

Development

yarn test

Tests use mongodb-memory-server for in-memory MongoDB testing.

Compatibility

  • mongoose 8.x / 9.x
  • TypeScript 5.0+
  • Node.js 18+

License

MIT