Enforcing Structure with MongoDB Schema Validation
Flexible documents are great until a bug writes price: "free" or forgets a required field. MongoDB can validate documents on every insert and update, regardless of which application wrote them.
Creating a collection with a validator
await db.createCollection('products', {
validator: {
$jsonSchema: {
bsonType: 'object',
required: ['name', 'price', 'status'],
properties: {
name: { bsonType: 'string', minLength: 1, maxLength: 200 },
price: { bsonType: ['int', 'long', 'double', 'decimal'], minimum: 0 },
status: { enum: ['draft', 'active', 'archived'] },
tags: { bsonType: 'array', items: { bsonType: 'string' }, maxItems: 20 },
dimensions: {
bsonType: 'object',
properties: { weightGrams: { bsonType: 'int', minimum: 0 } },
},
},
},
},
validationLevel: 'strict',
validationAction: 'error',
});
What a failure looks like
try {
await db.collection('products').insertOne({ name: 'Mug', price: -1, status: 'live' });
} catch (err) {
console.log(err.code); // 121 = DocumentValidationFailure
console.log(err.errInfo.details); // which rule failed and why
}
Adding validation to an existing collection
await db.command({
collMod: 'products',
validator: { $jsonSchema: productSchema },
validationLevel: 'moderate', // existing invalid docs can still be updated
validationAction: 'warn', // log violations instead of rejecting, at first
});
Roll out safely: start with warn, check the logs, fix bad data, then switch to error. Find violating documents with db.products.find({ $nor: [{ $jsonSchema: productSchema }] }).
Database vs application validation
- Application validation (Zod, Mongoose, Pydantic) gives friendly error messages to users.
- Database validation is the last line of defence against bugs, scripts and other services writing bad data.
- Use both for important collections; keep the database rules focused on types and essential fields.
Was this article helpful?
Comments
No comments yet. Have a question or a tip? Be the first to share it.