Defensive Modeling: Schema Validation
A common myth is that MongoDB has no schema. While it is flexible, production applications require structure. To enforce this, MongoDB uses JSON Schema Validation at the database level.
1. The Gatekeeper: JSON Schema
You define validation rules when you create a collection (or update it). These rules act as a gatekeeper: no document can enter unless it follows the rules.
[!WARNING] Performance Cost: Validation happens on every write. While fast, extremely complex regex patterns in your schema can slow down insert speeds.
Interactive: The Validator
Test if a document passes the schema rules.
- 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.
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("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{
"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. This is useful when introducing validation to an existing collection with “messy” data.
| 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. |
[!TIP] Use
validationAction: "warn"when you first deploy a schema to production. This lets you monitor logs for violations without breaking the application for users.