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

mondel

v0.2.1

Published

Lightweight TypeScript ORM for MongoDB - Type-safe, serverless-optimized

Readme


Overview

Mondel is a lightweight TypeScript ORM designed for MongoDB, optimized for serverless environments like Cloudflare Workers, Vercel Edge Functions, and AWS Lambda. It provides a 100% type-safe, intuitive API inspired by Prisma and Drizzle, with minimal bundle size and zero cold-start overhead.

Features

  • 100% Type-Safe — Schema names, field names, and return types strictly typed with full inference
  • Serverless First — Optimized for Cloudflare Workers & Vercel Edge with minimal bundle size (~27KB)
  • Zero Magic — No decorators, no reflection. Just pure TypeScript functions and predictability
  • MongoDB Native — Full access to MongoDB driver options (upsert, sessions, transactions)
  • Zod Integration — Built-in runtime validation with Zod schemas
  • Intuitive API — Prisma-inspired CRUD operations, Drizzle-inspired schema definition

Installation

npm install mondel mongodb zod
yarn add mondel mongodb zod
pnpm add mondel mongodb zod

Requirements

  • Node.js 18+
  • MongoDB 6.0+
  • TypeScript 5.0+ (recommended)

Quick Start

1. Define Your Schema

import { defineSchema, s } from "mondel";

export const userSchema = defineSchema("users", {
  timestamps: true,
  fields: {
    // _id is implicit - auto-generated by MongoDB and typed as ObjectId
    email: s.string().required().unique().index({ name: "idx_email" }),
    name: s.string(),
    role: s.enum(["ADMIN", "USER", "GUEST"]).default("USER"),
    age: s.number().min(0).max(150),
    isActive: s.boolean().default(true),
  },
});

// Export schemas array for client initialization
export const schemas = [userSchema] as const;

2. Create Client (Serverless Mode)

import { createClient, type SchemasToClient } from "mondel";
import { schemas } from "./schemas";

// Type for the connected client
export type DbClient = SchemasToClient<typeof schemas>;

// Create serverless client factory (no connection yet)
const connectDb = createClient({
  serverless: true,
  schemas,
  syncIndexes: false,
  validation: "strict",
});

// Connect when needed (e.g., in request handler)
export async function getDb(uri: string): Promise<DbClient> {
  return connectDb(uri);
}

3. Perform CRUD Operations

const db = await getDb(env.MONGODB_URI);

// Create
const result = await db.users.create({
  email: "[email protected]",
  name: "John Doe",
  role: "USER",
});

// Read with type-safe field selection
const found = await db.users.findOne(
  { email: "[email protected]" },
  { select: { _id: true, email: true, name: true } }
);

// Update with MongoDB native options
await db.users.updateOne(
  { email: "[email protected]" },
  { $set: { name: "John Smith" } },
  { upsert: true }
);

// Delete
await db.users.deleteOne({ email: "[email protected]" });

// Close connection when done
await db.close();

Type Safety

// ✅ Works - 'users' is a registered schema
db.users.findMany({});

// TypeScript error - 'rooms' doesn't exist
db.rooms.findMany({}); // Property 'rooms' does not exist

// TypeScript error - 'invalidField' doesn't exist
db.users.findMany({}, { select: { invalidField: true } });

Schema Definition

Define your schemas with a fluent, type-safe builder:

import { defineSchema, s } from "mondel";

const productSchema = defineSchema("products", {
  timestamps: true,
  fields: {
    // _id is implicit - auto-generated by MongoDB
    sku: s.string().required().index({ unique: true, name: "idx_sku" }),
    name: s.string().required().index({ type: "text" }),
    price: s.number().required().min(0),
    stock: s.number().default(0),
    category: s.string().required(),
    tags: s.array(s.string()),
    location: s
      .object({
        type: s.literal("Point"),
        coordinates: s.array(s.number()),
      })
      .index({ type: "2dsphere", name: "idx_location" }),
  },
  indexes: [
    {
      fields: { category: 1, price: -1 },
      options: { name: "idx_category_price" },
    },
  ],
});

Field Types

| Type | Builder | Description | | -------- | ------------------ | -------------------------------------- | | String | s.string() | String field with optional validations | | Number | s.number() | Numeric field with min/max | | Boolean | s.boolean() | Boolean field | | Date | s.date() | Date field | | ObjectId | s.objectId() | MongoDB ObjectId | | Array | s.array(items) | Array of typed items | | Object | s.object(props) | Nested object | | JSON | s.json() | Arbitrary JSON data | | Enum | s.enum([...]) | String enum validation | | Literal | s.literal(value) | Literal value |

Field Modifiers

s.string()
  .required() // Field is mandatory
  .unique() // Unique constraint
  .default("value") // Default value
  .index() // Create index
  .index({ name: "idx" }) // Named index
  .index({ type: "text" }) // Text index
  .min(1)
  .max(100) // Length constraints (string/number)
  .email() // Email validation
  .url() // URL validation
  .pattern(/regex/); // Regex pattern

Index Types

// Simple index
email: s.string().index()

// Named index
email: s.string().index({ name: "idx_email" })

// Unique index
email: s.string().index({ unique: true })

// Text index (full-text search)
description: s.string().index({ type: "text" })

// Geospatial index
location: s.object({...}).index({ type: "2dsphere" })

// TTL index
expiresAt: s.date().index({ expireAfterSeconds: 3600 })

// Compound indexes (schema level)
indexes: [
  { fields: { category: 1, price: -1 }, options: { name: "idx_cat_price" } }
]

Multiple Connections

// Create separate clients for different databases
const mainDb = await createClient({
  uri: process.env.MAIN_DB_URI,
  schemas: [userSchema, orderSchema] as const,
});

const analyticsDb = await createClient({
  uri: process.env.ANALYTICS_DB_URI,
  schemas: [eventSchema, metricSchema] as const,
  syncIndexes: false, // Don't sync indexes on replica
});

// Use them independently
const users = await mainDb.users.findMany({});
const events = await analyticsDb.events.findMany({});

Automatic Validation

Validation happens automatically inside CRUD methods - you don't need to call it manually:

const db = await createClient({
  uri: "mongodb://localhost:27017/mydb",
  schemas: [userSchema] as const,
  validation: "strict", // "strict" | "loose" | "off"
});

// TypeScript catches type errors at compile time
await db.users.create({
  email: "[email protected]",
  name: 123, // ❌ TypeScript error: expected string
});

// Runtime validation catches invalid data
await db.users.create({
  email: "invalid-email", // ❌ Runtime error: invalid email format
  name: "Test",
});

Validation modes:

  • "strict" (default): Throws error on validation failure
  • "loose": Logs warning but continues
  • "off": No runtime validation (TypeScript still enforces types)

Transactions

Full support for MongoDB transactions via db.startSession():

const db = await createClient({
  uri: process.env.MONGODB_URI,
  schemas: [userSchema, orderSchema] as const,
});

const session = db.startSession();

try {
  await session.withTransaction(async () => {
    const user = await db.users.create({ email: "[email protected]", balance: 100 }, { session });

    await db.orders.create({ userId: user.insertedId, amount: 50 }, { session });

    await db.users.updateById(user.insertedId, { $inc: { balance: -50 } }, { session });
  });
} finally {
  await session.endSession();
}

API Reference

defineSchema(name, definition)

Creates a schema definition with full type inference.

createClient(config)

Creates a database client. Supports two modes:

Serverless Mode (recommended for Cloudflare Workers, Vercel Edge):

const connectDb = createClient({
  serverless: true,
  schemas: [userSchema] as const,
  syncIndexes: false,
  validation: "strict",
});

// Returns a factory function - call with URI to connect
const db = await connectDb(env.MONGODB_URI);

Node.js Mode (for traditional servers):

const db = await createClient({
  uri: process.env.MONGODB_URI,
  schemas: [userSchema] as const,
  syncIndexes: true,
  validation: "strict",
});

Collection Methods

All methods accept MongoDB native options as the last parameter.

| Method | Description | | ----------------------------------- | ------------------------- | | findOne(where, options?) | Find single document | | findMany(where?, options?) | Find multiple documents | | findById(id) | Find by ObjectId | | create(data, options?) | Insert single document | | createMany(data[], options?) | Insert multiple documents | | updateOne(where, data, options?) | Update single document | | updateMany(where, data, options?) | Update multiple documents | | updateById(id, data, options?) | Update by ObjectId | | deleteOne(where) | Delete single document | | deleteMany(where) | Delete multiple documents | | deleteById(id) | Delete by ObjectId | | count(where?) | Count documents | | exists(where) | Check if document exists | | aggregate(pipeline) | Run aggregation pipeline |

Documentation

Full documentation is available at https://mondel-orm.pages.dev

Contributing

Contributions are welcome! Please read our Contributing Guide for details.

License

MIT © Edjo