Type-safe MongoDB ODM on top of official driver

MongoDB with Types!

Mongster keeps the MongoDB driver's mental model intact, then layers runtime validation, typed aggregation, typed populate, hooks, and transaction-scoped models on top.

npm i mongodb mongster

Single schema declaration

Typed aggregations builders

Populate with a single trip

models.ts
import { ,  } from "mongster";

const  = .({
  : .().(1).(100),
  : .().(),
}).();

export const  = ("users", );

const  = .({
  : .().(1).(200),
  : .().(false),
  : .().(() => ),
}).();

export const  = ("todos", );

Strict enough to matter

Schema-first w/o hiding MongoDB

Validation, refs, default values, timestamps, and index metadata stay in one definition.

Official driver underneath

No fake relational mental model. Just MongoDB, sharpened for TypeScript.

Why Mongster ?

Features that you need

Capabilities that feel most different in practice:
The ones that remove duplicated types, keep MongoDB expressive,
and still give you guardrails

One schema, many outputs

M.schema drives runtime validation, stored document types, input types, refs, timestamps, and index metadata from one place.

Read section

Typed aggregation builder

Group, project, unwind, and lookup without dropping back to loose result shapes every time a pipeline changes.

Read section

Transaction-scoped models

Use ctx.use(Model) inside a transaction and keep the same mental model while sessions are wired automatically.

Read section

Typed populate

Declare "relations" with objectId().ref(() => Model), then populate with typed selection paths instead of guesswork.

Read section

Hooks without magic fog

Schema and model hooks stay explicit, sequential, and useful for auditing, normalization, and derived writes.

Read section

Single source of truth

One schema fans out into the parts that usually drift

Inspired by some of the best schema-centric TypeScript libraries, but tuned for MongoDB. The same definition powers runtime validation, document types, input types, refs, and indexes.

import {  } from "mongodb";
import { ,  } from "mongster";

const  = .({
  : .().(1),
});
const  = ("users", );

const  = .({
  : .().(1).(200),
  : .().(false),
  : .().(() => ),
  : .().(),
}).();

type  = M.<typeof >;
type  = M.<typeof >;
const  = ("todos", );

const :  = {
  : "Ship docs",
  : "ship-docs",
  : new ("64f8e6a25e6f0a1c2d3b4e5f"),
};

await .();

Single source of truth

Reject bad writes before they hit MongoDB

Rules, defaults, refs, and timestamps stay in the schema, so invalid payloads fail before they become messy data.

One write path enforces field rules and default values.
Ref fields stay explicit at the boundary instead of becoming loose strings.
Timestamps remain automatic without extra DTO plumbing.

This is the contract your handlers feel immediately: fewer parallel validators, less drift.

Fits your stack

Built for modern TypeScript apps

Use Mongster where MongoDB teams already live: API servers, TanStack Start, Next.js, or lean edge apps that still want strong type guarantees.

Official MongoDB driver core

Keep MongoDB semantics instead of hiding them under a second abstraction stack.

Open guide

Hono and API edges

Schema-first models and runtime validation fit lightweight APIs without ceremony.

Open guide

TanStack Start apps

Use Mongster in server functions while keeping return shapes serializable and typed.

Open guide

Next.js apps

Drop models into server-side code, keep MongoDB close, and still get disciplined TS ergonomics.

Open guide

Get moving fast

From installation to first typed query in 3 moves

Landing pages should reduce hesitation. These are the first three actions most visitors want before they commit to reading the full docs.

01

Terminal

Install on top of the real driver

npm i mongodb mongster

Mongster wraps the official MongoDB driver instead of replacing it with a separate persistence model.

02

Schema

Define one schema

import { M, model } from "mongster";

const todoSchema = M.schema({
  title: M.string().min(1).max(200), // [!code highlight]
  completed: M.boolean().default(false), // [!code highlight]
  slug: M.string().uniqueIndex(), // [!code highlight]
}).withTimestamps(); // [!code highlight]

export const TodoModel = model("todos", todoSchema);

Add validation rules, refs, defaults, and indexes once, then let types and runtime checks follow.

03

Query

Query with Mongo semantics

import {  } from "./models";

const  = await .({ : false })
  .(["title", "createdAt"]) // [!code highlight]
  .({ : -1 })
  .(10);

// prettier-ignore
const  = await .({ : true })
  .("ownerId", { // [!code highlight]
    : ["name", "email"], // [!code highlight]
  });

const firstOwner = [0].;
const firstOwner: {
    name: string;
    email: string;
    _id: ObjectId;
} | null

Keep Mongo-style filters, add typed projections, populate, aggregation, hooks, and transaction helpers.

Start strict. Stay fast.

Build on MongoDB w/o giving up type integrity.

Read the quick start, inspect the API surface, or drop Mongster into your next TypeScript app and let one schema hold the line.