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

@iamcalegari/mongoat

v1.3.0

Published

A lightweight ODM library for MongoDB

Readme


Mongoat is a thin, extensible, and type-safe ODM (Object Document Mapper) for MongoDB in Node.js/TypeScript. It sits on top of the official mongodb driver without hiding it: full CRUD on typed models, schemas as plain objects or decorators, server-side JSON Schema validation, pre/post transformation hooks, versioned migrations with a CI-ready CLI, and Proxy-based method gating — productivity of an ODM while keeping full control of the native driver.

Table of Contents


Features

  • Thin ODM — a typed layer over the official mongodb driver, not a replacement for it
  • Full CRUD on typed models: insert/insertMany, find/findById/findMany, update/updateMany, delete/deleteMany, total, aggregate, bulkWrite
  • Schemas as objects or decorators — a plain $jsonSchema-shaped object, or a class with @Schema/@Prop; both compile to the same server-side validator
  • Pre/post hooks for transforming documents and reacting to operation results
  • Server-side validation via MongoDB $jsonSchema — enforced by the database, not just at the app layer
  • Production-ready migrations — versioned up/down files, transactional runs, a distributed lock, and a CLI built for CI (--dry-run, status --json, tiered exit codes)
  • Injection-safe by design — an always-on $where guard, opt-in sanitizeFilter for untrusted input, sanitized error hierarchy (MongoatError and subclasses)
  • Native escape hatchgetCollection()/getClient()/getDb() for direct, unrestricted access to the native driver whenever you need it
  • Type-safe end to end, with generics tied to your document schema
  • Dual CJS/ESM package, zero required runtime dependencies beyond mongodb/bson

Installation

npm install @iamcalegari/mongoat

yarn add @iamcalegari/mongoat

pnpm add @iamcalegari/mongoat

Requires Node.js ^20.19.0 || >=22.12.0. The mongodb v7 driver comes along as a regular dependency — no separate install.

Quick Start

Connecting to MongoDB

import { Database } from '@iamcalegari/mongoat';

export const database = new Database({
  dbName: 'mongoat-example',
});

await database.connect();

DatabaseConfig extends the driver's own MongoClientOptions, so anything the MongoClient accepts can be set here and is forwarded on connect(). Mongoat supplies defaults and your config overrides them; the only default is ignoreUndefined: true.

Mongoat does not configure MongoDB's Stable API for you — that is the application's call, made explicitly:

import { Database, ServerApiVersion } from '@iamcalegari/mongoat';

export const database = new Database({
  dbName: 'mongoat-example',
  serverApi: { version: ServerApiVersion.v1, strict: true },
});

strict: true makes the server reject every command outside Stable API v1. $vectorSearch, createSearchIndex and listSearchIndexes are all outside it, so an application using Atlas Vector Search must leave strict off:

MongoServerError: $vectorSearch is not allowed with 'apiStrict: true' in API Version 1
code: 323, codeName: APIStrictError

Defining a Model

import { Model, METHODS } from '@iamcalegari/mongoat';
import type {
  CreateIndexProps,
  ModelValidationSchema,
  SchemaWithDefaults,
} from '@iamcalegari/mongoat';

interface UserSchema {
  username: string;
  password: string;
  mail: string;
  firstName: string;
  lastName: string;
}

const schema: ModelValidationSchema<SchemaWithDefaults<UserSchema>> = {
  bsonType: 'object',
  properties: {
    username: { bsonType: 'string', description: 'Username of the user' },
    password: { bsonType: 'string', description: 'Password of the user' },
    mail: {
      bsonType: 'string',
      description: 'Mail of the user',
      pattern: '^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$',
    },
    firstName: { bsonType: 'string', description: 'First name of the user' },
    lastName: { bsonType: 'string', description: 'Last name of the user' },
    insertedAt: { bsonType: 'date', description: 'Date of the user creation' },
    updatedAt: {
      bsonType: 'date',
      description: 'Date of last update of the user',
    },
  },
  required: ['firstName', 'lastName', 'mail', 'password', 'username'],
};

const indexes: CreateIndexProps[] = [
  { key: { username: 1, mail: 1 }, name: 'unique_username_mail', unique: true },
];

export const User = new Model<UserSchema>({
  collectionName: 'users',
  schema,
  indexes,
  validity: true,
});

// Pre-hook: runs before every insert — a fresh timestamp per document.
User.pre(METHODS.INSERT, (ctx) => {
  ctx.document.password = 'hashedPassword';
  (ctx.document as SchemaWithDefaults<UserSchema>).insertedAt = new Date();
});

Prefer decorators? The same schema can be a class — every @Prop field is required unless marked @Optional(), and @Schema('users') supplies the collection name:

import { Model, Optional, Prop, Schema } from '@iamcalegari/mongoat';

@Schema('users')
class UserSchema {
  @Prop({ bsonType: 'string', description: 'Username of the user' })
  username!: string;

  @Prop({
    bsonType: 'string',
    pattern: '^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$',
  })
  mail!: string;

  @Optional()
  @Prop({ bsonType: ['int', 'null'] })
  age?: number;
}

export const User = new Model<UserSchema>({
  schema: UserSchema,
  validity: true,
});

See Define a schema with decorators for the full mapping between the two forms.

Basic CRUD Usage

await database.setupCollections();

const user = await User.insert({
  username: 'foobar',
  mail: '[email protected]',
  password: 'strongPassword',
  firstName: 'Foo',
  lastName: 'Bar',
});

await User.update({ _id: user._id }, { $set: { firstName: 'John' } });

const users = await User.findMany();

await User.delete({ username: 'foobar' });

await database.disconnect();

Migrations

The package ships a mongoat CLI for versioned, transactional migrations:

npx mongoat create backfill-user-status   # scaffold migrations/<timestamp>_backfill-user-status.ts
npx mongoat up                            # apply pending migrations, in order
npx mongoat status                        # applied/pending overview

Every run executes inside a MongoDB transaction (replica set required) under a distributed lock, so concurrent deploys can't double-apply. For CI there are --dry-run, status --json, and tiered exit codes. Migrations written in TypeScript need tsx as an optional peer dependency; .js migrations need nothing extra.

Full Documentation

Full documentation → https://iamcalegari.github.io/mongoat/

The site is the source of truth for guides, API reference, and the migration guide — this README only covers the essentials to get started:

  • Tutorials — guided quick start and your first migration
  • How-to guides — decorators, hooks, migrations, sanitizing untrusted filters, error handling, the native escape hatch, indexes & validation
  • Reference — full public API, generated from source
  • CLI reference — the mongoat migration CLI
  • Explanation — design philosophy, Proxy gating, server-side validation, the migration lock
  • Benchmarks — measured against the native driver, Mongoose, and Papr
  • Stability & versioning — semver policy, what's covered by the public API contract
  • Migration guide — upgrading from the alpha line to v1.0

Contributing

Issues and pull requests are welcome — see open issues or open a new one before starting significant work.

License

MIT