A MongoDB ORM with exceptional developer experience, built for TypeScript.
- Perfect Type Inference - S+ tier TypeScript support with zero
anytypes - Flexible Relations - EMBED (denormalized), LOOKUP (virtual joins), and REFERENCE strategies
- Auto-updating Embeds - Optional
keepFreshmode keeps embedded data synchronized - Intuitive API - Clean, modern syntax with excellent IntelliSense
- Context Support - Built-in multi-tenancy and auth context handling
- Transaction Support - First-class transaction API
- Zero Runtime Overhead - Compile-time type checking with minimal runtime cost
npm install @mizzle-dev/orm mongodb
# or
pnpm add @mizzle-dev/orm mongodb
# or
yarn add @mizzle-dev/orm mongodbRequirements: Node.js 20+ and MongoDB driver 6.0+
import { mizzle, defineSchema, mongoCollection } from '@mizzle-dev/orm';
import { string, objectId, date } from '@mizzle-dev/orm';
import { lookup } from '@mizzle-dev/orm';
// Define collections
const users = mongoCollection('users', {
name: string(),
email: string(),
createdAt: date(),
});
const posts = mongoCollection(
'posts',
{
title: string(),
content: string(),
authorId: objectId(),
createdAt: date(),
},
{
relations: {
author: lookup(users, {
localField: 'authorId',
foreignField: '_id',
one: true,
}),
},
}
);
// Create schema and connect
const schema = defineSchema({ users, posts });
const db = await mizzle({
uri: 'mongodb://localhost:27017',
dbName: 'myapp',
schema,
});
// Create data
const user = await db().users.create({
name: 'Alice',
email: 'alice@example.com',
createdAt: new Date(),
});
const post = await db().posts.create({
title: 'Hello Mizzle!',
content: 'My first post',
authorId: user._id,
createdAt: new Date(),
});
// Query with perfect type inference
const posts = await db().posts.findMany(
{},
{
include: { author: true },
}
);
// TypeScript knows exact types!
posts[0].title; // string
posts[0].author?.name; // string | undefined
posts[0].author?.email; // string | undefinedMizzle supports three relation strategies:
Denormalize data for lightning-fast reads with optional auto-updates:
import { embed } from '@mizzle-dev/orm';
const posts = mongoCollection('posts', {
title: string(),
authorId: objectId(),
}, {
relations: {
author: embed(users, {
forward: {
from: 'authorId',
fields: ['name', 'email'],
},
keepFresh: true, // Auto-update when user changes
}),
},
});
// Author data embedded automatically!
const post = await db().posts.create({
title: 'Hello World',
authorId: userId,
});
console.log(post.author.name); // Direct access, no join neededBenefits:
- Fast reads (no joins)
- Simple queries
- Optional auto-updates with
keepFresh - Perfect for read-heavy workloads
Query-time joins using MongoDB $lookup:
import { lookup } from '@mizzle-dev/orm';
author: lookup(users, {
localField: 'authorId',
foreignField: '_id',
one: true,
})Benefits:
- Always fresh data
- Less storage
- Best for frequently-changing data
Validate referential integrity:
import { reference } from '@mizzle-dev/orm';
author: reference(users, {
localField: 'authorId',
foreignField: '_id',
})Pass context for auth and multi-tenancy:
// With context
const userPosts = await db({
user: { id: userId, role: 'admin' },
tenantId: 'acme-corp'
}).posts.findMany({});
// Without context
const allPosts = await db().posts.findMany({});Built-in transaction support:
await db.tx({}, async (txDb) => {
const user = await txDb().users.create({ name: 'Bob' });
const post = await txDb().posts.create({
title: 'New Post',
authorId: user._id
});
// Committed atomically
});Keep embedded data fresh automatically:
author: embed(users, {
forward: {
from: 'authorId',
fields: ['name', 'avatar']
},
keepFresh: true, // Updates automatically when user changes
})Refresh embeds on-demand:
// Query-time refresh (read-only)
const posts = await db().posts.findMany(
{ status: 'published' },
{ refreshEmbeds: ['author'] }
);
// Batch refresh (persisted)
await db().posts.refreshEmbeds('author', {
filter: { updatedAt: { $lt: yesterday } },
batchSize: 100,
});Unlimited depth with perfect type inference:
const posts = await db().posts.findMany({}, {
include: {
author: {
include: {
organization: true
}
},
comments: {
include: {
user: true
}
}
}
});
// All types perfectly inferred!
posts[0].author?.organization?.name // string | undefined
posts[0].comments[0]?.user?.email // string | undefined| Scenario | Best Choice | Why |
|---|---|---|
| Blog post authors | EMBED + keepFresh |
Fast reads, occasional updates |
| E-commerce orders | EMBED (no auto-update) | Historical snapshot |
| Real-time stock prices | LOOKUP | Always need latest data |
| User permissions | LOOKUP | Changes frequently |
| Tag clouds | EMBED + keepFresh |
Fast display, rare changes |
Check out the examples directory for comprehensive demonstrations:
- quickstart.ts - 5-minute tutorial
- blog-with-embeds.ts - Blog platform with auto-updating embeds
- ecommerce-orders.ts - Order system with historical snapshots
- mizzle-api-example.ts - Advanced usage patterns
- Complete Documentation - Full API reference and guides
- Embed Relations Guide - Deep dive into embed strategies
- Migration Guide - Converting from lookups to embeds
- Performance at Scale - Guidelines for large schemas
// Create
const user = await db().users.create({ name: 'Alice' });
const users = await db().users.createMany([...]);
// Read
const user = await db().users.findOne({ email: 'alice@example.com' });
const users = await db().users.findMany({ active: true });
const users = await db().users.findMany({}, { include: { posts: true } });
// Update
await db().users.updateOne({ _id: userId }, { name: 'Alice Updated' });
await db().users.updateMany({ active: false }, { deleted: true });
// Delete
await db().users.deleteOne({ _id: userId });
await db().users.deleteMany({ deleted: true });
// Aggregations
const result = await db().users.aggregate([...]);
// Raw access
const collection = db().users.collection; // Native MongoDB collectiondb.schema // Collection definitions
db.client // Raw MongoClient
db.tx // Transaction helper
db.close() // Cleanup connectionMizzle provides exceptional TypeScript support:
- Zero
anytypes in your queries - Perfect inference for nested includes
- Compile-time safety for all operations
- Full IntelliSense support
- Type-safe filters and projections
Read Performance:
- EMBED: ~50-100ms for 1000 documents
- LOOKUP: ~200-500ms for 1000 documents
Recommendation: Use EMBED for read-heavy workloads, LOOKUP for write-heavy or when data changes frequently.
Contributions are welcome! Please check out our GitHub repository.
- Fork the repository
- Create your feature branch
- Commit your changes
- Push to the branch
- Open a Pull Request
MIT © Mizzle Dev
Built with love for the MongoDB + TypeScript community