Mongoose 9.6.0 was released on April 28, 2026 and includes several noteworthy new features.
The biggest new feature is the new allowNull schema type option, which lets you disallow null without making a path required.
Mongoose has long allowed null for optional paths by default, which makes sense for MongoDB's flexible document model.
But TypeScript and JSON Schema users often need a sharper distinction between "missing" and "explicitly null".
In this blog post, I'll cover how allowNull works, how it differs from required, and how it affects TypeScript inference and JSON Schema output.
Disallowing null Without required
By default, Mongoose allows both null and undefined (nullish values) for paths that are not required.
For example, the following schema allows documents where name is missing, set to undefined, or set to null.
const userSchema = new mongoose.Schema({
name: String
});
const User = mongoose.model('User', userSchema);
await new User({}).validate(); // OK
await new User({ name: undefined }).validate(); // OK
await new User({ name: null }).validate(); // OK
allowNull: false changes that behavior.
With allowNull: false, undefined still passes validation, but null does not.
const userSchema = new mongoose.Schema({
name: {
type: String,
allowNull: false
}
});
const User = mongoose.model('User', userSchema);
await new User({}).validate(); // OK
await new User({ name: undefined }).validate(); // OK
const err = await new User({ name: null }).validate().catch(err => err);
err.errors['name'].kind; // 'allowNull'
err.errors['name'].message; // 'Path `name` does not allow null values.'
The name: undefined case seems a little strange, but remember that Mongoose strips out keys that are set to undefined since v6.
There's no way to store a key with a value that is strictly equal to undefined in Mongoose - MongoDB's BSON spec has marked undefined as deprecated for over 15 years.
const doc = new User({ name: undefined });
await doc.save();
const fromDb = await User.findById(doc._id).orFail();
Object.keys(fromDb); // ['_id'] does **NOT** contain `name`
This is useful when your application distinguishes between "not provided" and "explicitly null".
Most notably, allowNull is to reduce friction for TypeScript developers: TypeScript developers are used to using name?: string for optional parameters.
Having to add name?: string | null or pass in name ?? undefined is a source of friction.
Historically, Mongoose did not distinguish between these two cases because the MongoDB Node driver would convert undefined values to null:
// Call `insertOne()` on the raw MongoDB Node driver collection, bypassing Mongoose
await User.collection.insertOne({ _id: 'test', name: undefined });
// { _id: 'test', name: null }
console.log(await User.collection.findOne({ _id: 'test' }));
However, given that Mongoose strips undefined values before they get to the MongoDB Node driver, we think allowNull is a reasonable addition to make TypeScript developers' lives easier.
How allowNull Differs From required
required: true means the path must be non-nullish: not null and not undefined.
allowNull: false only disallows null.
It does not make the path required.
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
nickname: {
type: String,
allowNull: false
}
});
const User = mongoose.model('User', userSchema);
const err = new User({
nickname: null
}).validateSync();
err.errors['name'].kind; // 'required'
err.errors['nickname'].kind; // 'allowNull'
Because required: true already disallows null, Mongoose does not allow you to set allowNull on a path with static required: true.
The following schema throws an error when you create it.
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
allowNull: false
}
});
// Throws: Path "name" may not have `allowNull` specified when `required` is true
The same rule applies if you call allowNull(false) and required(true) at runtime.
const userSchema = new mongoose.Schema({ name: String });
userSchema.path('name').allowNull(false);
userSchema.path('name').required(true); // Throws
However, allowNull can be useful with function-based required.
Function-based required only applies when the function returns a truthy value, so allowNull: false lets you disallow null even when the path is not currently required.
const taskSchema = new mongoose.Schema({
status: String,
endDate: {
type: Date,
required: function() {
return this.status === 'completed';
},
allowNull: false
}
});
const Task = mongoose.model('Task', taskSchema);
await new Task({
status: 'pending'
}).validate(); // OK, endDate is undefined
let err = await new Task({
status: 'pending',
endDate: null
}).validate().catch(err => err);
err.errors['endDate'].kind; // 'allowNull'
err = await new Task({
status: 'completed',
endDate: null
}).validate().catch(err => err);
err.errors['endDate'].kind; // 'required'
In the last example, required wins when the task is completed because Date paths consider null missing for required validation.
When the task is pending, the path is not required, but allowNull: false still rejects null.
TypeScript Inference
Mongoose 9.6 also updates TypeScript inference to understand allowNull: false.
By default, optional paths include null in the inferred type.
With allowNull: false, optional paths do not include null.
import mongoose from 'mongoose';
const userSchema = new mongoose.Schema({
name: {
type: String,
allowNull: false
},
age: Number
});
type User = mongoose.InferSchemaType<typeof userSchema>;
// User is roughly:
// {
// name?: string;
// age?: number | null;
// }
name is still optional because allowNull: false does not make the path required.
But name no longer allows null.
Meanwhile, age still allows null because it uses the default Mongoose behavior for optional paths.
The same logic applies to InferRawDocType.
const schemaDefinition = {
name: {
type: String,
allowNull: false
},
title: String
} as const;
type RawUser = mongoose.InferRawDocType<typeof schemaDefinition>;
// RawUser is roughly:
// {
// name?: string;
// title?: string | null;
// }
This is the part of allowNull that makes it especially useful in TypeScript codebases.
Before Mongoose 9.6, if you wanted TypeScript to know that an optional path could be undefined but not null, you typically needed to define a separate document interface manually.
Now Mongoose can infer that distinction from your schema definition.
JSON Schema Output
allowNull also affects toJSONSchema().
By default, optional paths include null in generated JSON Schema output.
With allowNull: false, Mongoose omits null from the path's type.
const userSchema = new mongoose.Schema({
name: {
type: String,
allowNull: false
},
age: Number
});
userSchema.toJSONSchema();
The above schema generates the following JSON Schema for name and age.
{
type: 'object',
required: ['_id'],
properties: {
_id: {
type: 'string'
},
name: {
type: 'string'
},
age: {
type: ['number', 'null']
}
}
}
name has type: 'string', because allowNull: false means null is not allowed.
age has type: ['number', 'null'], because optional paths allow null by default.
The same applies if you use BSON types.
userSchema.toJSONSchema({ useBsonType: true });
{
required: ['_id'],
properties: {
_id: {
bsonType: 'objectId'
},
name: {
bsonType: 'string'
},
age: {
bsonType: ['number', 'null']
}
}
}
This makes allowNull a good fit for apps that use Mongoose schemas to generate MongoDB collection validators or OpenAPI-compatible schema output.
Moving On
allowNull is a small option, but it fills a long-standing gap in Mongoose validation.
You can now model "optional, but not null" directly in your schema without custom validators, custom TypeScript interfaces, or post-processing JSON Schema output.
Mongoose 9.6 also upgrades the MongoDB Node.js driver to 7.2 and includes several TypeScript improvements, including better QueryFilter support for string unions and enums.
Make sure you upgrade to take advantage of the new features!


