Why a Schemaless Database Still Needs Validation
MongoDB doesn't require every document in a collection to share a fixed shape, which is powerful during rapid iteration but risky in production: a typo'd field name, a missing required attribute, or a string stored where a number was expected can silently corrupt data that downstream code assumes is well-formed. $jsonSchema validation, configured at the collection level via db.createCollection or collMod, lets you declare structural rules — required fields, types, value ranges, enum constraints — that MongoDB enforces on every insert and update, while still allowing fields outside the schema unless you explicitly forbid them with additionalProperties: false. This gives you a middle ground: schema flexibility where you want it, hard guarantees where you need them.
Cricket analogy: A cricket board doesn't dictate every player's exact technique, but it does mandate hard rules like bat dimensions and helmet standards; $jsonSchema works the same way, allowing flexible document shape while enforcing non-negotiable structural rules like required fields.
Writing a $jsonSchema Validator
A validator is a JSON Schema document (draft-4 style, with some MongoDB-specific extensions like bsonType) attached to a collection's validator option. You specify bsonType: 'object', a required array of field names, and a properties object describing each field's expected bsonType, and optionally constraints like minimum, maximum, pattern (a regex), or enum. MongoDB checks this validator on every insert and every update that would leave the document in a non-conforming state, rejecting the operation with a descriptive error unless validationAction is set to warn instead of the default error. You can layer validationLevel: 'moderate' to only validate new inserts and updates to already-valid documents, which is useful when retrofitting validation onto a collection that already has some non-conforming legacy data.
Cricket analogy: Ball-tracking technology like Hawk-Eye checks a specific set of required conditions, ball height, impact point, before ruling LBW, rejecting any decision that doesn't meet the defined criteria — just as a validator rejects any document missing required fields or failing type checks.
Validation Actions and Migration Strategy
Adding validation to an existing, populated collection needs care, because turning on strict error validation immediately can break writes to legacy documents that predate the schema. The recommended migration path is: first add the validator with validationAction: 'warn' so violations are logged to the server log but not blocked, run a script to identify and backfill or fix non-conforming documents, then flip validationAction to 'error' once the collection is clean. validationLevel: 'moderate' (versus 'strict', the default) is another lever — it applies the validator only to inserts and to updates on documents that already satisfy the schema, leaving untouched legacy documents alone until something modifies them, which is gentler than a hard cutover.
Cricket analogy: A cricket board phasing in a new bat-thickness regulation typically warns players for one season before enforcing bans, giving teams time to replace non-compliant equipment, just as validationAction: 'warn' gives time before switching to 'error'.
db.createCollection("orders", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["customerId", "total", "status"],
properties: {
customerId: { bsonType: "objectId" },
total: { bsonType: "double", minimum: 0 },
status: {
enum: ["pending", "paid", "shipped", "cancelled"],
description: "must be one of the allowed order statuses"
},
couponCode: {
bsonType: "string",
pattern: "^[A-Z0-9]{4,12}$"
}
}
}
},
validationAction: "warn", // start soft, migrate legacy data, then switch to "error"
validationLevel: "moderate"
});
// Later, once legacy data is clean:
db.runCommand({
collMod: "orders",
validationAction: "error",
validationLevel: "strict"
});$jsonSchema validators run on every write path — insertOne, insertMany, updateOne, updateMany, findAndModify — so you get consistent enforcement regardless of which part of the application (or which service) is writing to the collection.
Validators are not retroactive: adding a strict validator to a collection does not check or fix documents already stored. Existing non-conforming documents remain until something writes to them, or until you run an explicit migration/backfill script.
- MongoDB's flexible schema doesn't mean no structure — $jsonSchema validators enforce required fields, types, and constraints per collection.
- Validators use bsonType, required, properties, enum, pattern, minimum, and maximum to describe rules.
- additionalProperties: false locks a document to exactly the declared fields; omitting it allows extra fields.
- validationAction can be 'warn' (log only) or 'error' (reject the write), useful for a phased rollout.
- validationLevel 'moderate' applies rules only to inserts and updates of already-valid documents; 'strict' applies to all writes.
- Validators are not retroactive — existing non-conforming documents are unaffected until they're next written to.
- A safe migration path is warn, backfill/fix legacy data, then switch to error.
Practice what you learned
1. What MongoDB feature lets you enforce required fields and types on a collection?
2. What does validationAction: 'warn' do?
3. What does validationLevel: 'moderate' do differently from 'strict'?
4. Are $jsonSchema validators retroactive on existing documents?
5. What constraint would you use to restrict a field to one of a fixed set of allowed string values?
Was this page helpful?
You May Also Like
Schema Design in MongoDB
Learn how to design MongoDB document schemas around your application's access patterns rather than around abstract normalized tables.
Data Modeling Patterns
A tour of named MongoDB schema design patterns — attribute, bucket, computed, subset, and extended reference — and the problems each one solves.
Working with Arrays and Nested Documents
Master querying and updating deeply nested MongoDB documents and arrays using dot notation, positional operators, and array update operators.