A thin wrapper around MongoDB that allows you to define your models using Zod schemas. This library provides type-safe MongoDB operations with runtime validation using Zod.
- Type-safe MongoDB operations
- Runtime validation using Zod schemas
- Automatic handling of
_id,createdAt, andupdatedAtfields - Full TypeScript support
- Comprehensive CRUD operations
- Strict type checking for filters and updates
npm install zod-mongoThis package requires the following peer dependencies:
zodmongodb
Make sure to install them in your project:
npm install zod mongodbimport { z } from "zod";
import { ZodMongoRepository, InferMongoDocument } from "zod-mongo";
// Define your schema using Zod
const userSchema = z.object({
name: z.string(),
email: z.string().email(),
age: z.number().optional(),
});
// Infer the document type from the schema, including _id and timestamp fields
type UserDocument = InferMongoDocument<typeof userSchema>;
// Create a repository
const userRepository = new ZodMongoRepository<UserDocument>({
collectionName: "users",
schema: userSchema,
timestamps: true, // Optional: Enable/disable automatic timestamp management (default: true)
});
// Create a user
const { doc: user } = await userRepository.insertOne({
name: "John Doe",
email: "john@example.com",
age: 30,
});
// Find a user
const foundUser = await userRepository.findOne({ email: "john@example.com" });
// Update a user
await userRepository.updateOne(
{ email: "john@example.com" },
{ $set: { age: 31 } }
);The repository automatically manages createdAt and updatedAt timestamps for your documents. This behavior can be configured when creating the repository:
// Disable automatic timestamp management
const noTimestampsRepo = new ZodMongoRepository({
collectionName: "users",
schema: userSchema,
timestamps: false,
});
// Enable automatic timestamp management (default)
const withTimestampsRepo = new ZodMongoRepository({
collectionName: "users",
schema: userSchema,
timestamps: true,
});When enabled, the repository will:
- Set both
createdAtandupdatedAtwhen inserting new documents - Update
updatedAtwhen modifying existing documents - Handle timestamps consistently across all operations (insert, update, findAndUpdate)
Before using the repository, you need to connect to your MongoDB database. Here's how to set it up:
import { MongoClient } from "mongodb";
import { ZodMongoDatabaseConnection } from "zod-mongo";
// Create MongoClient
const client = new MongoClient("mongodb://localhost:27017");
// Setup the database connection
await ZodMongoDatabaseConnection.setup({
client,
dbName: "my-database",
});// Listen for connection events
ZodMongoDatabaseConnection.on("connected", () => {
console.log("Connected to MongoDB");
});
ZodMongoDatabaseConnection.on("disconnected", () => {
console.log("Disconnected from MongoDB");
});
ZodMongoDatabaseConnection.on("error", (error) => {
console.error("MongoDB connection error:", error);
});You can extend the ZodMongoRepository class to create custom repositories with domain-specific methods:
import { z } from "zod";
import { ZodMongoDocument, ZodMongoRepository } from "zod-mongo";
// Define your schema
const userProfileSchema = z.object({
userId: z.string(),
hasCompletedOnboarding: z.boolean(),
});
type UserProfile = InferMongoDocument<typeof userProfileSchema>;
// Create a custom repository
class UserProfileRepository extends ZodMongoRepository<UserProfile> {
constructor() {
super({
collectionName: "user_profiles",
schema: userProfileSchema,
});
}
// Custom method to get or create a profile
async getOrCreateProfile(userId: string): Promise<UserProfile> {
const existingProfile = await this.findOne({ userId });
if (existingProfile) {
return existingProfile;
}
const { doc } = await this.insertOne({
userId,
hasCompletedOnboarding: false,
});
return doc;
}
}
// Usage example
async function main() {
const profileRepo = new UserProfileRepository();
// Get or create a profile
const profile = await profileRepo.getOrCreateProfile("user123");
}The repository provides methods similar to the MongoDB client, plus some extra features to make things easier:
Standard MongoDB methods:
insertOne(input, options?): Insert a single documentinsertMany(input[], options?): Insert multiple documentsfindOne(filter, options?): Find a single documentfind(filter, options?): Get a cursor for streaming resultsfindOneAndUpdate(filter, update, options?): Find and update a documentupdateOne(filter, update, options?): Update a single documentupdateMany(filter, update, options?): Update multiple documentsdeleteOne(filter, options?): Delete a single documentdeleteMany(filter, options?): Delete multiple documentscountDocuments(filter, options?): Counts the number of documentsdistinct(key, filter, options?): Retrieves distinct values for a specified field
Additional methods
findOneStrict(filter, options?): Find a single document (throws if not found)findMany(filter, options?): Find multiple documents (returns an array of results)exists(filter, options?): Check if documents exist
In some cases, you might need to access the MongoDB collection directly to perform operations that are not covered by the ZodMongoRepository methods. For such cases, you can use the collection method, which provides direct access to the underlying MongoDB collection.
// Access the MongoDB collection directly
const collection = await userRepository.collection();
// Use the collection to perform MongoDB operations
const stats = await collection.stats();This method serves as an escape hatch for advanced use cases where you need more control over the MongoDB operations.
ZodMongoSchema<T>: Input type for documents (excludes_id,createdAt,updatedAt)ZodMongoDocument<T>: Complete document type including MongoDB fields
The repository is fully type-safe, meaning:
- All operations are typed with your schema
- Filters and updates are strictly typed
- Return types are properly inferred
- Runtime validation ensures data integrity
Contributions are welcome! Please feel free to submit a Pull Request.
MIT