DocsHub
Schema Design

Schema Validation

Learn how to enforce data quality in MongoDB using $jsonSchema validation rules — required fields, types, value ranges, and string patterns.

Schema Validation

MongoDB is schema-flexible by default — you can insert any document into any collection without defining a structure first. This is great for getting started quickly, but in a real application, you need to ensure data quality. A student document without a name, or an age stored as a string, or a grade value that does not match your expected values — these cause bugs that are hard to track down.

Schema validation lets you define rules at the database level. MongoDB enforces these rules on every insert and update. If a document does not follow the rules, MongoDB rejects it with a clear error.


How Validation Works

You define validation rules when creating a collection, or add them to an existing collection using collMod. Rules are written using $jsonSchema — a standard way to describe the shape and constraints of a document.

When a document is inserted or updated, MongoDB checks it against the schema. If it passes — the operation succeeds. If it fails — MongoDB rejects it and returns an error.


Creating a Collection with Validation

Use db.createCollection() with a validator option:

db.createCollection("students", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["name", "age", "grade", "enrolled"],
      properties: {
        name: {
          bsonType: "string",
          description: "Student name is required and must be a string"
        },
        age: {
          bsonType: "int",
          minimum: 5,
          maximum: 25,
          description: "Age must be an integer between 5 and 25"
        },
        grade: {
          bsonType: "string",
          enum: ["9th", "10th", "11th", "12th"],
          description: "Grade must be one of 9th, 10th, 11th, 12th"
        },
        enrolled: {
          bsonType: "bool",
          description: "Enrolled must be a boolean"
        },
        email: {
          bsonType: "string",
          pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
          description: "Email must be a valid email address"
        }
      }
    }
  }
})

Now every insert into students must follow these rules.


$jsonSchema — Key Properties

bsonType

Specifies the expected BSON type of the field:

bsonType valueMatches
"string"String values
"int"32-bit integers
"double"Decimal numbers
"bool"Boolean true/false
"date"Date values
"objectId"ObjectId values
"array"Array values
"object"Embedded document
"null"Null values

required

An array of field names that must be present in every document:

required: ["name", "age", "grade", "enrolled"]

If any of these fields is missing, the insert is rejected.

properties

Defines rules for individual fields:

properties: {
  fieldName: {
    bsonType: "string",
    // ... other rules
  }
}

minimum and maximum

For number fields — the allowed range of values:

age: {
  bsonType: "int",
  minimum: 5,
  maximum: 25
}

minLength and maxLength

For string fields — the allowed length range:

name: {
  bsonType: "string",
  minLength: 2,
  maxLength: 100
}

enum

A list of allowed values. Only these exact values are accepted:

grade: {
  bsonType: "string",
  enum: ["9th", "10th", "11th", "12th"]
}

pattern

A regular expression the string must match:

email: {
  bsonType: "string",
  pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
}

Validating Embedded Documents

You can validate nested objects too:

db.createCollection("students", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["name", "age", "grade"],
      properties: {
        name: { bsonType: "string" },
        age: { bsonType: "int", minimum: 5, maximum: 25 },
        grade: {
          bsonType: "string",
          enum: ["9th", "10th", "11th", "12th"]
        },

        // Validate the embedded address object
        address: {
          bsonType: "object",
          required: ["city", "country"],
          properties: {
            city:    { bsonType: "string" },
            country: { bsonType: "string" }
          }
        },

        // Validate the embedded guardian object
        guardian: {
          bsonType: "object",
          required: ["name", "phone"],
          properties: {
            name:     { bsonType: "string" },
            phone:    { bsonType: "string" },
            relation: {
              bsonType: "string",
              enum: ["Father", "Mother", "Brother", "Sister", "Uncle", "Aunt", "Other"]
            }
          }
        }
      }
    }
  }
})

Validating Arrays

Validate an array field and the type of its elements:

subjects: {
  bsonType: "array",
  minItems: 1,
  maxItems: 10,
  items: {
    bsonType: "string"
  },
  description: "Subjects must be an array of 1 to 10 strings"
}

Validate an array of objects:

grades: {
  bsonType: "array",
  items: {
    bsonType: "object",
    required: ["subject", "score"],
    properties: {
      subject: { bsonType: "string" },
      score: {
        bsonType: "int",
        minimum: 0,
        maximum: 100
      },
      semester: {
        bsonType: "int",
        enum: [1, 2]
      }
    }
  }
}

What Happens When Validation Fails

If you insert a document that violates the schema:

// Missing required field 'grade'
db.students.insertOne({
  name: "Ali Hassan",
  age: 16,
  enrolled: true
})

MongoDB throws:

MongoServerError: Document failed validation
Additional information: {
  failingDocumentId: ObjectId("..."),
  details: {
    operatorName: "$jsonSchema",
    schemaRulesNotSatisfied: [
      {
        operatorName: "required",
        specifiedAs: { required: ["name", "age", "grade", "enrolled"] },
        missingProperties: ["grade"]
      }
    ]
  }
}

The error tells you exactly which rule failed and why. Wrap inserts in try/catch in your Node.js code:

try {
  await students.insertOne({
    name: "Ali Hassan",
    age: 16,
    enrolled: true
    // grade is missing
  });
} catch (error) {
  if (error.code === 121) {
    console.error("Document failed validation:", error.errInfo.details);
  }
}

Error code 121 means document validation failure.


Adding Validation to an Existing Collection

If your collection already exists and has data, use collMod to add validation:

db.runCommand({
  collMod: "students",
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["name", "age", "grade"],
      properties: {
        name:    { bsonType: "string" },
        age:     { bsonType: "int", minimum: 5, maximum: 25 },
        grade: {
          bsonType: "string",
          enum: ["9th", "10th", "11th", "12th"]
        },
        enrolled: { bsonType: "bool" }
      }
    }
  },
  validationLevel: "moderate",  // only validate new/updated docs
  validationAction: "error"     // reject invalid documents
})

validationLevel

Controls which documents are validated:

LevelBehavior
"strict"Validate all inserts and updates — default
"moderate"Validate inserts and updates to documents that already pass validation. Skip validation for documents that already fail.

Use "moderate" when adding validation to an existing collection that may have some invalid documents. This way, old invalid documents are not blocked from being updated.

validationAction

Controls what happens when validation fails:

ActionBehavior
"error"Reject the document — default
"warn"Allow the document but log a warning

Use "warn" when you want to see what would fail before enforcing strict rules — useful during migration.


Viewing Validation Rules

To see the validation rules on a collection:

db.getCollectionInfos({ name: "students" })

Output includes the full validator definition.


Removing Validation

To remove all validation rules from a collection:

db.runCommand({
  collMod: "students",
  validator: {},
  validationLevel: "off"
})

Complete School System Validation

Here is the full validation schema for our students collection:

db.createCollection("students", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["name", "age", "grade", "enrolled", "enrollmentDate"],
      properties: {

        name: {
          bsonType: "string",
          minLength: 2,
          maxLength: 100,
          description: "Required. Student full name."
        },

        age: {
          bsonType: "int",
          minimum: 5,
          maximum: 25,
          description: "Required. Age between 5 and 25."
        },

        grade: {
          bsonType: "string",
          enum: ["9th", "10th", "11th", "12th"],
          description: "Required. Must be a valid grade."
        },

        enrolled: {
          bsonType: "bool",
          description: "Required. Enrollment status."
        },

        enrollmentDate: {
          bsonType: "date",
          description: "Required. Must be a Date type."
        },

        email: {
          bsonType: "string",
          pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
          description: "Optional. Must be a valid email if provided."
        },

        address: {
          bsonType: "object",
          required: ["city", "country"],
          properties: {
            city:    { bsonType: "string" },
            country: { bsonType: "string" }
          }
        },

        guardian: {
          bsonType: "object",
          required: ["name", "phone"],
          properties: {
            name:     { bsonType: "string" },
            phone:    { bsonType: "string" },
            relation: {
              bsonType: "string",
              enum: ["Father", "Mother", "Brother", "Sister", "Uncle", "Aunt", "Other"]
            }
          }
        },

        subjects: {
          bsonType: "array",
          minItems: 1,
          maxItems: 10,
          items: { bsonType: "string" }
        },

        grades: {
          bsonType: "array",
          items: {
            bsonType: "object",
            required: ["subject", "score"],
            properties: {
              subject:  { bsonType: "string" },
              score: {
                bsonType: "int",
                minimum: 0,
                maximum: 100
              },
              semester: {
                bsonType: "int",
                enum: [1, 2]
              }
            }
          }
        }

      }
    }
  },
  validationAction: "error",
  validationLevel: "strict"
})

Quick Reference

RuleWhat it doesExample
requiredFields that must existrequired: ["name", "age"]
bsonTypeExpected type of fieldbsonType: "string"
minimum / maximumNumber rangeminimum: 0, maximum: 100
minLength / maxLengthString length rangeminLength: 2, maxLength: 100
enumAllowed valuesenum: ["9th", "10th", "11th"]
patternRegex the string must matchpattern: "^[a-z]+$"
minItems / maxItemsArray size rangeminItems: 1, maxItems: 10
itemsRules for array elementsitems: { bsonType: "string" }
propertiesRules for object fieldsproperties: { city: { bsonType: "string" } }

Add validation rules gradually — start with just the required fields and critical type checks. Once those are working correctly, add more specific rules like enums, patterns, and ranges. Trying to write a perfect schema on day one usually leads to overly strict rules that break legitimate use cases.

On this page