CRUD and querying
Add schema validation
Use JSON Schema validation for stable invariants while preserving deliberate flexibility between document shapes.
9 minute lesson
MongoDB has a flexible schema, not an absent schema. While you prototype, that flexibility is a feature. Once the application stabilizes, some fields become invariants: every animal has a name, age is always a number. Schema validation lets the database enforce exactly those rules while leaving the rest of the document free.
Why put checks in the database when the application already validates? Because the application is not the only writer. Imports, migration scripts, admin tooling, and the teammate poking around in mongosh all bypass application code. The application produces friendly error messages; the database guarantees the invariant.
Validation is expressed as a $jsonSchema and attached when creating a collection:
db.createCollection('animals', {
validator: {
$jsonSchema: {
required: ['name', 'age'],
properties: {
name: { bsonType: 'string' },
age: { bsonType: 'int', minimum: 0 }
}
}
}
})
Now try one valid and one invalid insert:
db.animals.insertOne({ name: 'Roger', age: 8 })
// { acknowledged: true, insertedId: ObjectId('...') }
db.animals.insertOne({ name: 'Buck', age: 'four' })
// MongoServerError: Document failed validation
The error includes a details object that names the failing rule — here age failed the bsonType check. Read it instead of guessing; with several rules it tells you exactly which one rejected the write.
Tightening a live collection
Adding validation to an existing collection uses collMod, and this is where planning matters. Existing documents are not checked when you add the validator — only future writes are. Before tightening, find out what would fail:
db.animals.countDocuments({ age: { $not: { $type: 'int' } } })
If old documents violate the new rule, an innocent update to an unrelated field on one of them can suddenly fail validation. That is the classic surprise: writes that worked yesterday start erroring after someone added a validator. Either fix the old documents first, or set validationLevel: 'moderate' so existing invalid documents can still be updated while new inserts must comply.
Keep the validator small. Enforce the invariants every valid write must respect, and leave deliberately flexible fields out of it.
Lesson completed