Playground
Getting Started

Usage

Learn how to use zod-mongoose in your application.

Usage

Basic Conversion

To convert a Zod schema into a Mongoose schema, use the toMongooseSchema function. This is the main entry point for using zod-mongoose in your backend application.

import { z } from 'zod/v4';
import { toMongooseSchema } from '@nullix/zod-mongoose';
import mongoose from 'mongoose';

// Define a Zod schema
const UserZodSchema = z.object({
  username: z.string().min(3),
  email: z.string().email(),
  age: z.number().optional(),
});

// Convert to Mongoose Schema
const UserSchema = toMongooseSchema(UserZodSchema);

// Create a Mongoose Model
const UserModel = mongoose.model('User', UserSchema);

Passing Mongoose Options

The second parameter of toMongooseSchema allows you to specify standard Mongoose schema options, such as timestamps, collection, and versionKey.

const mongooseSchema = toMongooseSchema(UserZodSchema, {
  timestamps: true,
  collection: 'users',
});

Extending Schemas

zod-mongoose makes it easy to extend your schemas with Mongoose plugins or custom logic using hooks.

Using Mongoose Plugins

You can apply standard Mongoose plugins directly during the conversion process.

import mongooseLeanVirtuals from 'mongoose-lean-virtuals';

const mongooseSchema = toMongooseSchema(zodSchema, {
  plugins: [mongooseLeanVirtuals]
});

Using Hooks

The schema:created hook allows you to modify the mongoose.Schema instance immediately after it is created.

import { hooks } from '@nullix/zod-mongoose';

hooks.hook('schema:created', ({ schema, zodSchema }) => {
  // Add a virtual field to all schemas that have a 'title' field
  if ('title' in zodSchema.shape) {
    schema.virtual('slug').get(function() {
      return this.title.toLowerCase().replace(/ /g, '-');
    });
  }
});

Advanced Type Handling

zod-mongoose provides specialized helpers for Mongoose-specific types.

ObjectIds and Buffers

Use zObjectId() and zBuffer() for fields that map to Mongoose ObjectId and Buffer types.

import { zObjectId, zBuffer } from '@nullix/zod-mongoose';

const ProductSchema = z.object({
  _id: zObjectId(), // Managed by Mongoose unless { includeId: true } is passed
  category: zObjectId({ ref: 'Category' }),
  imageData: zBuffer(),
});

Populated Fields

For fields that can be either an ObjectId (unpopulated) or a full object (populated), use zPopulated.

import { zPopulated } from '@nullix/zod-mongoose';

const PostSchema = z.object({
  author: zPopulated('User', UserZodSchema),
});

Adding Mongoose Metadata

Use withMongoose to add Mongoose-specific options like unique, index, or default.

import { withMongoose } from '@nullix/zod-mongoose';

const UserZodSchema = z.object({
  email: withMongoose(z.string().email(), { 
    unique: true, 
    index: true 
  }),
});

Isomorphic Support (Frontend Mode)

In modern applications, you often want to share your Zod schemas between your backend and your frontend. To do this, enable "frontend mode" on your client.

import { setFrontendMode } from '@nullix/zod-mongoose';

// On the frontend, enable frontend mode
setFrontendMode(true);

// Now schemas using zObjectId() will fall back to string validation
// and zBuffer() will fall back to Uint8Array, without requiring Mongoose.

Using with Nuxt 4 & Nitro

zod-mongoose is optimized for Nuxt 4. You can use your Zod schemas directly with readValidatedBody in your Nitro server routes.

// server/api/users.post.ts
import { UserZodSchema } from '~/schemas/user';

export default defineEventHandler(async (event) => {
  const body = await readValidatedBody(event, UserZodSchema.parse);
  
  // body is now fully typed and validated by Zod
  const user = await UserModel.create(body);
  return user;
});