6.3 Schema Validation with JSON Schema
Key Takeaways
- MongoDB provides server-side schema validation via '$jsonSchema', enforcing document structure, mandatory fields, and value ranges during writes.
- The 'bsonType' keyword enforces strict BSON types ('string', 'int', 'long', 'double', 'decimal', 'objectId', 'date') beyond standard JSON primitives.
- 'validationLevel' controls scope: 'strict' (default) evaluates all writes, while 'moderate' validates inserts and updates to valid docs while exempting legacy invalid docs.
- 'validationAction' determines failure handling: 'error' (default) rejects the write with DocumentValidationFailure (Code 121), whereas 'warn' logs the violation to mongod.log.
- Validation rules are modified dynamically via the 'collMod' command, and setting 'additionalProperties: false' requires explicitly declaring '_id' in properties.
6.3 Schema Validation with JSON Schema
While MongoDB is renowned for its flexible, dynamic document model, enterprise applications require robust data governance, type safety, and structural consistency. Rather than relying solely on client-side Object-Document Mappers (ODMs) like Mongoose or Spring Data—which can be bypassed by direct database connections or microservices written in different languages—MongoDB provides Server-Side Schema Validation.
Schema validation is defined at the collection level using the $jsonSchema operator. Validation rules are enforced natively by the mongod storage engine whenever an insertOne(), insertMany(), updateOne(), updateMany(), replaceOne(), or findAndModify() operation executes.
1. The $jsonSchema Validator Syntax
MongoDB supports the JSON Schema Draft 4 standard, extended with MongoDB-specific keywords—most notably bsonType.
+-----------------------------------------------------------------------------+
| $jsonSchema Core Keywords |
| |
| - bsonType : Enforces specific BSON types ('string', 'int'...) |
| - required : Array of mandatory field names |
| - properties : Map defining validation rules for specific fields |
| - additionalProperties : Boolean / Schema controlling undeclared fields |
| - enum : Array of permitted literal values |
| - minimum / maximum : Inclusive numeric boundary constraints |
| - pattern : Regular expression string match |
| - items : Schema validator applied to array elements |
| - description : Human-readable error/documentation string |
+-----------------------------------------------------------------------------+
Standard BSON Type Identifiers (bsonType)
In JSON Schema, types are typically generic ("number", "string", "object"). In MongoDB $jsonSchema, developers can specify exact BSON type aliases:
"string""int"(32-bit signed integer)"long"(64-bit signed integer)"double"(64-bit IEEE floating-point)"decimal"(128-bit decimal floating-point /Decimal128)"bool""objectId""array""object""date""timestamp""null"
Exam Trap: In the MongoDB Shell (
mongosh), writing{ age: 30 }creates a 64-bit float (double) by default. If a$jsonSchemarule requiresbsonType: "int", inserting{ age: 30 }without explicit casting (such asNumberInt(30)) will fail schema validation! To allow any numeric type, pass an array of types:bsonType: ["int", "long", "double", "decimal"]orbsonType: "number".
2. Defining Schema Validation with db.createCollection()
When initializing a new collection, schema validation rules are supplied inside the options document via the validator parameter:
db.createCollection("users", {
validator: {
$jsonSchema: {
bsonType: "object",
title: "User Document Validation",
required: [ "username", "email", "status", "role", "created_at" ],
additionalProperties: true, // Allows unlisted optional fields
properties: {
_id: {
bsonType: "objectId"
},
username: {
bsonType: "string",
minLength: 3,
maxLength: 30,
description: "'username' must be a string between 3 and 30 characters and is required"
},
email: {
bsonType: "string",
pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
description: "'email' must match standard email regex format and is required"
},
status: {
enum: [ "PENDING", "ACTIVE", "SUSPENDED", "ARCHIVED" ],
description: "'status' can only be one of the enum values and is required"
},
role: {
enum: [ "USER", "MANAGER", "ADMIN" ],
description: "'role' must be USER, MANAGER, or ADMIN"
},
age: {
bsonType: "int",
minimum: 18,
maximum: 120,
description: "'age' must be an integer between 18 and 120"
},
created_at: {
bsonType: "date",
description: "'created_at' must be a BSON Date and is required"
},
tags: {
bsonType: "array",
minItems: 1,
maxItems: 10,
uniqueItems: true,
items: {
bsonType: "string"
},
description: "'tags' must be an array of up to 10 unique strings"
},
address: {
bsonType: "object",
required: [ "street", "city", "zip" ],
properties: {
street: { bsonType: "string" },
city: { bsonType: "string" },
zip: { bsonType: "string", pattern: "^[0-9]{5}(-[0-9]{4})?$" }
}
}
}
}
},
validationLevel: "strict",
validationAction: "error"
});
3. Validation Levels: strict vs. moderate
The validationLevel option determines which write operations are subject to validation checks:
| Validation Level | Applies to insert Operations | Applies to Updates on Valid Documents | Applies to Updates on Existing Invalid Docs |
|---|---|---|---|
strict (Default) | Enforced | Enforced | Enforced (Update fails if doc remains invalid) |
moderate | Enforced | Enforced | Skipped (Allows updating legacy non-compliant docs) |
off | Disabled | Disabled | Disabled |
+-----------------------------------------------------------------------------+
| validationLevel Execution Flow |
| |
| 1. INCOMING INSERT: |
| Both 'strict' and 'moderate' ALWAYS validate new documents. |
| |
| 2. INCOMING UPDATE: |
| - Document ALREADY conforms to schema: |
| --> Both 'strict' and 'moderate' validate the modified document. |
| |
| - Document was PREVIOUSLY NON-COMPLIANT (Legacy data before validator):|
| --> 'strict': REJECTS the update unless the update fixes all errors. |
| --> 'moderate': PERMITS the update to proceed without error! |
+-----------------------------------------------------------------------------+
When to Use moderate
When introducing schema validation to an existing production database containing legacy documents that violate the new rules, setting validationLevel: "moderate" allows application updates to existing legacy documents without breaking live services, while guaranteeing that all new inserts and updates to modern documents strictly adhere to the schema.
4. Validation Actions: error vs. warn
The validationAction option controls what action MongoDB takes when a write operation violates the validation rules:
| Validation Action | Behavior on Violation | Write Operation Result | Logging & Diagnostic Visibility |
|---|---|---|---|
error (Default) | Rejects write immediately | Throws DocumentValidationFailure (Code 121) | Write is aborted; nothing written to disk |
warn | Accepts write to storage | Write succeeds with acknowledgment | Logs validation violation warning to mongod.log |
Production Migration Strategy with warn
When deploying a new schema validation rule to a mission-critical system, best practice follows a phased migration path:
- Deploy with
validationAction: "warn". - Monitor server diagnostic logs (
mongod.log) for validation warning entries to identify misbehaving client services. - Fix client-side data serialization bugs.
- Promote
validationActionto"error"to enforce hard database-level rejection.
5. Modifying Existing Validators with collMod
Collections cannot be redefined with createCollection() once they exist. To update schema validators, change validation levels, or toggle validation actions on a live collection, use the administrative collMod (Collection Modify) command via db.runCommand().
Syntax for collMod
db.runCommand({
collMod: "users", // Target collection name
validator: {
$jsonSchema: {
bsonType: "object",
required: [ "username", "email", "status", "tier" ], // Added 'tier' as required
properties: {
username: { bsonType: "string" },
email: { bsonType: "string" },
status: { enum: [ "ACTIVE", "INACTIVE" ] },
tier: { enum: [ "BRONZE", "SILVER", "GOLD", "PLATINUM" ] }
}
}
},
validationLevel: "moderate",
validationAction: "error"
});
Inspecting Active Collection Validation Rules
To inspect the active validator, validationLevel, and validationAction on a collection, query db.getCollectionInfos():
// In mongosh:
db.getCollectionInfos({ name: "users" });
// Returns:
[
{
name: "users",
type: "collection",
options: {
validator: { $jsonSchema: { ... } },
validationLevel: "moderate",
validationAction: "error"
},
info: { readOnly: false }
}
]
Completely Removing Validation from a Collection
To remove validation entirely, pass an empty object {} as the validator in collMod:
db.runCommand({
collMod: "users",
validator: {},
validationLevel: "off"
});
6. Testing and Handling Validation Errors
When a write violates schema rules under validationAction: "error", MongoDB halts the write and returns an error payload with error code 121 (DocumentValidationFailure):
// Attempting an invalid insert (missing required 'email' and invalid 'status')
try {
db.users.insertOne({
username: "john_doe",
status: "INVALID_STATUS", // Not in enum
role: "USER",
created_at: new Date()
});
} catch (e) {
console.error("Write failed:", e.message);
// MongoServerError: Document failed validation
// Error Code: 121 (DocumentValidationFailure)
}
The additionalProperties Keyword
additionalProperties: true(Default if omitted): Documents can contain arbitrary fields not declared in thepropertiesmap.additionalProperties: false: Documents are strictly prohibited from containing any fields not explicitly listed inproperties.
Exam Trap: When setting
additionalProperties: false, you must include the_idfield in thepropertiesobject (e.g.,_id: { bsonType: "objectId" }). If_idis omitted fromproperties, every document insertion will fail validation because MongoDB drivers automatically inject_idbefore transmission!
A collection has schema validation enabled with 'validationLevel: "moderate"' and 'validationAction: "error"'. The collection contains an existing legacy document that was inserted before validation was configured and does NOT conform to the schema. An application issues an 'updateOne()' command modifying a valid field on this legacy document. What is the result?
A database administrator configures a collection with 'validationAction: "warn"'. When an application attempts to insert a document that violates the required fields defined in '$jsonSchema', what occurs?
Which MongoDB administrative command is used to add or modify schema validation rules on an existing collection without dropping it?
A collection validator defines a rule requiring: 'age: { bsonType: "int", minimum: 21 }'. When executing an insert in the MongoDB Shell using 'db.users.insertOne({ age: 25 })', the write fails with a DocumentValidationFailure error. Why did this validation failure occur?