Core Functions
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
SchemaOptionsplus 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()unlesscollectionis provided. It is used to generate unique discriminator model names such asActivity_clicked.
- validateBeforeSave: When enabled (the default), parses the document in a Mongoose
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: Includesmodelname and documentid.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
MongooseMetaobject.
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 oldermongooseZodCustomType()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.