Defensive Modeling: Schema Validation
Imagine running a high-traffic e-commerce platform. One day, a buggy mobile client update starts sending user profiles where the age field is stored as the string "twenty-two" instead of the integer 22. Your reporting dashboards crash, your analytics pipelines fail, and downstream services start throwing Null Pointer Exceptions.
A common myth is that MongoDB has no schema. While it is fundamentally flexible and allows for rapid iteration, production applications require rigid structure to prevent malformed data from polluting the database. To enforce this, MongoDB uses JSON Schema Validation at the database level.
1. The Gatekeeper: JSON Schema
Think of JSON Schema Validation as an uncompromising bouncer at an exclusive club. Before any document is allowed inside the collection, the bouncer checks its “ID” against a strict list of rules. If a document is missing required fields, has the wrong data types, or falls outside acceptable ranges, the bouncer rejects it at the door.
You define these validation rules when you create a collection (or update an existing one). These rules ensure that even if application-level validation fails or is bypassed, your database remains pristine.
Performance Cost: Validation happens on every write. While fast, evaluating extremely complex regex patterns or massively nested schemas on every insert can introduce latency. Keep your rules targeted to critical data boundaries.
Interactive: The Validator
Test if a document passes the schema rules for our users collection.
- Name: Required String.
- Age: Integer ≥ 18.
- Role: Must be one of
["user", "admin", "moderator"]. - Email: Must match regex
^.+@mongodb\.com$.
2. Defining Rules in Code
Most developers define these rules in their application migration scripts, not manually in the shell. The structure follows the standard JSON Schema draft 4 specification. The rules are declared as a BSON document under the <code>$jsonSchema</code> operator.
Java
import com.mongodb.client.model.ValidationOptions;
import com.mongodb.client.model.CreateCollectionOptions;
import org.bson.Document;
// Define schema as a Document
Document jsonSchema = new Document("bsonType", "object")
.append("required", Arrays.asList("name", "email", "age", "role"))
.append("properties", new Document()
.append("name", new Document()
.append("bsonType", "string")
.append("description", "must be a string and is required"))
.append("age", new Document()
.append("bsonType", "int")
.append("minimum", 18)
.append("description", "must be an integer >= 18"))
.append("role", new Document()
.append("bsonType", "string")
.append("enum", Arrays.asList("user", "admin", "moderator")))
.append("email", new Document()
.append("bsonType", "string")
.append("pattern", "^.+@mongodb\\.com$")));
// Apply options
ValidationOptions valOptions = new ValidationOptions()
.validator(new Document("$jsonSchema", jsonSchema));
CreateCollectionOptions createOptions = new CreateCollectionOptions()
.validationOptions(valOptions);
db.createCollection("users", createOptions);
Go
import "go.mongodb.org/mongo-driver/mongo/options"
// Define the JSON Schema as BSON
jsonSchema := bson.M{
"bsonType": "object",
"required": []string{"name", "email", "age", "role"},
"properties": bson.M{
"name": bson.M{
"bsonType": "string",
"description": "must be a string and is required",
},
"age": bson.M{
"bsonType": "int",
"minimum": 18,
"description": "must be an integer >= 18",
},
"role": bson.M{
"bsonType": "string",
"enum": []string{"user", "admin", "moderator"},
},
"email": bson.M{
"bsonType": "string",
"pattern": "^.+@mongodb\\.com$",
"description": "must match regex pattern",
},
},
}
validator := bson.M{"$jsonSchema": jsonSchema}
opts := options.CreateCollection().SetValidator(validator)
err := db.CreateCollection(ctx, "users", opts)
3. Validation Levels & Actions
You can control how strict the database is when applying rules. This is especially useful when introducing validation to an existing collection with years of “messy” legacy data. Think of this as training a new bouncer—sometimes you want them to aggressively turn non-compliant people away at the door, but during a transition period, you might just want them to take notes on who is breaking the dress code so you can fix it later.
| Setting | Option | Behavior |
|---|---|---|
| validationLevel | strict (Default) |
Validate all inserts and updates. |
moderate |
Validate inserts and updates to valid documents. Ignore updates to already-invalid documents. | |
| validationAction | error (Default) |
Reject the write. |
warn |
Allow the write but log a warning in the MongoDB log file. |
Use validationAction: "warn" when you first deploy a new schema to a production database. This acts as an audit mode, allowing you to monitor logs for violations without breaking the application for users. Once you have identified and backfilled the non-compliant upstream data, you can safely flip the action to "error".