Collection Operations
CRUD operations with TypedCollection
Overview
The TypedCollection class wraps MongoDB's native Collection with type-safe methods. All operations are fully typed based on your schema definition.
Creating a Collection
Use defineCollection to create a typed collection:
import { defineCollection } from "momos";
import { z } from "zod";
const postSchema = z.object({
title: z.string(),
content: z.string(),
author: z.string(),
tags: z.array(z.string()),
views: z.number().default(0),
published: z.boolean().default(false),
createdAt: z.date().default(() => new Date()),
});
const posts = defineCollection(db, "posts", postSchema);Insert Operations
insertOne
Insert a single document. The document is validated before insertion.
const result = await posts.insertOne({
title: "Hello World",
content: "This is my first post",
author: "john",
tags: ["intro", "hello"],
});
console.log(result.insertedId); // ObjectIdThe _id field is optional - MongoDB will generate one if not provided:
// Providing a custom _id
await posts.insertOne({
_id: new ObjectId("507f1f77bcf86cd799439011"),
title: "With Custom ID",
content: "...",
author: "john",
tags: [],
});insertMany
Insert multiple documents at once. All documents are validated before insertion.
const result = await posts.insertMany([
{
title: "Post 1",
content: "Content 1",
author: "alice",
tags: ["tag1"],
},
{
title: "Post 2",
content: "Content 2",
author: "bob",
tags: ["tag2"],
},
]);
console.log(result.insertedCount); // 2
console.log(result.insertedIds); // { 0: ObjectId, 1: ObjectId }Find Operations
find
Find documents matching a filter. Returns a typed cursor for chaining operations.
// Find all published posts
const cursor = posts.find({ published: true });
const publishedPosts = await cursor.toArray();
// Chain cursor methods
const recentPosts = await posts
.find({ author: "john" })
.sort({ createdAt: -1 })
.limit(10)
.toArray();
// Use filters with operators
const popularPosts = await posts
.find({
views: { $gte: 1000 },
tags: { $in: ["popular", "trending"] },
})
.toArray();findOne
Find a single document matching the filter.
const post = await posts.findOne({ title: "Hello World" });
if (post) {
console.log(post.title); // Type-safe access
console.log(post._id); // ObjectId is always present
}findById
Convenience method to find a document by its _id.
import { ObjectId } from "mongodb";
const post = await posts.findById(new ObjectId("507f1f77bcf86cd799439011"));
// Also accepts string IDs
const post2 = await posts.findById("507f1f77bcf86cd799439011");Update Operations
updateOne
Update a single document matching the filter.
const result = await posts.updateOne(
{ title: "Hello World" },
{ $set: { views: 100 } }
);
console.log(result.matchedCount); // 1
console.log(result.modifiedCount); // 1updateMany
Update all documents matching the filter.
const result = await posts.updateMany(
{ author: "john" },
{ $set: { published: true } }
);
console.log(result.modifiedCount);replaceOne
Replace an entire document. The replacement is validated before saving.
const result = await posts.replaceOne(
{ _id: postId },
{
title: "Updated Title",
content: "Completely new content",
author: "john",
tags: ["updated"],
views: 0,
published: false,
createdAt: new Date(),
}
);findOneAndUpdate
Find a document and update it atomically. Returns the document (before or after update).
// Return the updated document
const updated = await posts.findOneAndUpdate(
{ title: "Hello World" },
{ $inc: { views: 1 } },
{ returnDocument: "after" }
);
if (updated) {
console.log(updated.views); // Incremented value
}findOneAndReplace
Find a document and replace it atomically. The replacement is validated.
const replaced = await posts.findOneAndReplace(
{ title: "Old Title" },
{
title: "New Title",
content: "New content",
author: "john",
tags: [],
views: 0,
published: true,
createdAt: new Date(),
},
{ returnDocument: "after" }
);Delete Operations
deleteOne
Delete a single document matching the filter.
const result = await posts.deleteOne({ title: "Hello World" });
console.log(result.deletedCount); // 1 or 0deleteMany
Delete all documents matching the filter.
const result = await posts.deleteMany({ published: false });
console.log(result.deletedCount);findOneAndDelete
Find a document and delete it atomically. Returns the deleted document.
const deleted = await posts.findOneAndDelete({ title: "To Delete" });
if (deleted) {
console.log("Deleted:", deleted.title);
}Count Operations
countDocuments
Count documents matching the filter.
const count = await posts.countDocuments({ published: true });
console.log(`${count} published posts`);
// With options
const recentCount = await posts.countDocuments(
{ createdAt: { $gte: lastWeek } },
{ limit: 1000 } // Stop counting after 1000
);estimatedDocumentCount
Get an estimated count using collection metadata. Faster than countDocuments but less accurate.
const estimate = await posts.estimatedDocumentCount();
console.log(`Approximately ${estimate} posts`);exists
Check if any document matches the filter.
const hasPublished = await posts.exists({ published: true });
if (hasPublished) {
console.log("At least one published post exists");
}Aggregation
Run aggregation pipelines with typed results.
// Count posts by author
const authorStats = await posts
.aggregate<{ _id: string; count: number }>([
{ $group: { _id: "$author", count: { $sum: 1 } } },
{ $sort: { count: -1 } },
])
.toArray();
// Type-safe results
authorStats.forEach((stat) => {
console.log(`${stat._id}: ${stat.count} posts`);
});Index Operations
createIndex
Create an index on the collection.
await posts.createIndex({ author: 1 });
// Compound index
await posts.createIndex({ author: 1, createdAt: -1 });
// With options
await posts.createIndex(
{ email: 1 },
{ unique: true }
);
// Text index
await posts.createIndex({ title: "text", content: "text" });createIndexes
Create multiple indexes at once.
await posts.createIndexes([
{ key: { author: 1 } },
{ key: { tags: 1 } },
{ key: { createdAt: -1 } },
]);dropIndex
Drop an index by name.
await posts.dropIndex("author_1");indexes
List all indexes on the collection.
const indexes = await posts.indexes();
indexes.forEach((index) => {
console.log(index.name, index.key);
});Utility Operations
distinct
Get distinct values for a field.
const authors = await posts.distinct("author");
// authors: string[]
const tags = await posts.distinct("tags", { published: true });
// tags: string[]drop
Drop the entire collection.
await posts.drop();Warning
This permanently deletes all documents in the collection. Use with caution!
Collection Properties
collectionName
Get the name of the collection.
console.log(posts.collectionName); // "posts"raw
Access the underlying MongoDB collection for operations not covered by the wrapper.
const rawCollection = posts.raw;
// Use native methods
await rawCollection.bulkWrite([
{ insertOne: { document: { ... } } },
{ updateOne: { filter: { ... }, update: { ... } } },
]);