v3.3.1
Playground
API Reference

Core Functions

The main functions for converting Zod schemas to Mongoose.

Core Functions

These functions form the core of the @nullix/zod-mongoose library, allowing you to convert Zod definitions into Mongoose-compatible schemas and definitions.

toMongooseSchema(zodSchema, options?)

Converts a Zod schema to a Mongoose schema instance (new mongoose.Schema(...)).

  • zodSchema: A Zod object or any Zod type.
  • options: Optional Mongoose SchemaOptions plus package options.
    • validateBeforeSave: When enabled (the default), parses the document in a Mongoose post('validate') hook to check Zod refinements. Parsed transform output is not written back to the document.
    • modelName: (String) The base Mongoose model name. Required for a top-level z.discriminatedUnion() unless collection is provided. It is used to generate unique discriminator model names such as Activity_clicked.
import { z } from 'zod/v4';
import { toMongooseSchema } from '@nullix/zod-mongoose';

const zodSchema = z.object({
  name: z.string(),
});

// Basic conversion with timestamps enabled
const mongooseSchema = toMongooseSchema(zodSchema, { timestamps: true });

// Disable automatic Zod validation
const mongooseSchemaNoVal = toMongooseSchema(zodSchema, { validateBeforeSave: false });

Mongoose schema options such as query, methods, and statics are typed from the Zod document and carried through to the compiled model:

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

const userSchema = z.object({ name: z.string() });

const UserModel = mongoose.model('User', toMongooseSchema(userSchema, {
  query: {
    byName(name: string) {
      return this.where({ name });
    },
  },
}));

UserModel.find().byName('Ada');

If you store options in a separately typed variable, parameterize ToMongooseSchemaOptions with the Zod output type and your Mongoose helper interfaces. This also preserves explicitly typed virtuals.

For a first model, follow Start Here. For older APIs, see Migration.

For a top-level discriminated union, pass modelName or collection in the conversion options. When only collection is passed, the base model name is derived from it. See Advanced Types.

Automatic Zod Validation

toMongooseSchema adds a Mongoose post('validate') hook that parses this.toObject() after excluding Mongoose-generated IDs, version keys, and timestamps absent from the corresponding Zod objects. This checks Zod refinements and other rules that cannot be mapped to Mongoose options. It does not assign the parsed result back to the document, so parse input before constructing a Mongoose document when you need a Zod transformation to affect stored values. See Validate Documents and Object Modes and Unknown Keys.

If a Zod validation fails, the hook throws an error with a JSON-formatted message containing:

  • context: Includes model name and document id.
  • errors: An array of Zod issues.

To disable this behavior, set validateBeforeSave: false in the schema options or via withMongoose metadata.

extractMongooseDef(zodSchema)

Converts a Zod schema to a Mongoose schema definition object (the POJO used as the first argument for new mongoose.Schema(...)). This is useful if you want to manually create the Mongoose schema, combine it with other definitions, or use it with Mongoose's .add() method.

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

const zodSchema = z.object({
  name: z.string(),
});

const definition = extractMongooseDef(zodSchema);
// Result: { name: { type: String, required: true } }

withMongoose(zodSchema, metadata)

Attaches Mongoose-specific metadata to a Zod schema instance. The converter reads relevant metadata as field options, root schema options, or nested subschema options depending on where the instance is used. MongooseMeta is a permissive metadata interface; it does not type-check every Mongoose option.

  • metadata: A MongooseMeta object.

For an object passed to toMongooseSchema(), put schema options directly in the metadata. For an object inside another object, put its subschema options under schema. In either place, _id: false removes the identifier path; id: false only disables the string id virtual.

Field-level metadata

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

const zodSchema = z.object({
  username: withMongoose(z.string(), {
    unique: true,
    index: true,
    lowercase: true
  }),
});

Schema-level metadata

You can also apply schema-level options (like disabling _id or setting a collection name) by wrapping the top-level Zod object.

const LogSchema = withMongoose(
  z.object({ message: z.string() }),
  { _id: false, id: false, collection: 'system_logs' }
);

Explicit Subschemas

By default, nested Zod objects are mapped to separate Mongoose subschemas (with their own _id, middleware, etc.). If you want a nested object to be a simple nested document (POJO), use schema: false.

const UserSchema = z.object({
  profile: withMongoose(
    z.object({ bio: z.string() }),
    { schema: false }
  ),
});

You can also pass SchemaOptions to the schema property to customize a nested subschema:

const UserSchema = z.object({
  profile: withMongoose(
    z.object({ bio: z.string() }),
    { schema: { _id: false, id: false, timestamps: true } }
  ),
});

See Nested Object IDs for a runnable comparison and Reusing Object Schemas for imports shared across files and arrays.

To define a custom Mongoose type, use withMongoose(zodSchema, { type: 'YourType' }). See Migration for the older mongooseZodCustomType() API.

Document and input types

Import z from zod/v4. The package exports InferDocument<T> for a document with a root ObjectId and InferInput<T> for data accepted by Zod. It does not replace Zod's z.infer or add IDs to nested types. See Model and Input Types.