JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 160
  • Score
    100M100P100Q72073F
  • License MIT

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

Package Exports

  • mondel

Readme

Mondel Logo

Mondel

Lightweight TypeScript ORM for MongoDB
Type-safe. Serverless-ready. Zero magic.

InstallationQuick StartFeaturesAPI ReferenceDocumentation


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
  • CLI Pull/Push — Pull schema from remote DB and push indexes/validators via scripts or CI

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,
  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: "john@example.com",
  name: "John Doe",
  role: "USER",
});

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

// Update with MongoDB native options
await db.users.updateOne(
  { email: "john@example.com" },
  { $set: { name: "John Smith" } },
  { upsert: true }
);

// Delete
await db.users.deleteOne({ email: "john@example.com" });

// 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,
});

// 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: "test@example.com",
  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: "john@example.com", 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,
  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,
  validation: "strict",
});

Schema Push/Pull CLI

Use npx mondel to synchronize indexes and MongoDB validators outside application startup.

Config file with TypeScript + dotenv:

import "dotenv/config";
import type { MondelCliConfig } from "mondel";

export default {
  uri: process.env.MONGODB_URI,
  pull: { outDir: "./src/db/schemas", perCollectionFiles: true },
  push: { schemaFile: "./dist/schemas.js", applyValidators: true },
} satisfies MondelCliConfig;
# Config-first usage
npx mondel pull --config ./mondel.config.ts
npx mondel push --config ./mondel.config.ts

# Direct usage without config
npx mondel pull --uri mongodb://localhost:27017/mydb --out ./src/db/pulled-schema.ts --format ts
npx mondel push --uri mongodb://localhost:27017/mydb --schema ./dist/schemas.js --apply-validators

syncIndexes still works for backward compatibility but is now deprecated.

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


Made with ❤️ for the MongoDB community