JSON Schema Validation in MongoDB

Last updated:

MongoDB supports document-level validation using $jsonSchema inside a collection validator. Unlike standalone JSON Schema files used with Ajv or similar libraries, MongoDB's validator lives inside the database engine itself — every insert and update is checked before the write is committed. This guide covers how to define a validator, use BSON-specific types, enforce required fields, and control when and how validation errors are reported.

Adding a Validator to a New Collection

Pass a validator option to db.createCollection(). The validator object contains a single $jsonSchema key whose value is a schema object. MongoDB evaluates this schema against every document on write.

db.createCollection("users", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      title: "User document validator",
      required: ["email", "name", "createdAt"],
      properties: {
        _id: {
          bsonType: "objectId"
        },
        email: {
          bsonType: "string",
          pattern: "^[^@]+@[^@]+\.[^@]+$",
          description: "Must be a valid email address"
        },
        name: {
          bsonType: "string",
          minLength: 1,
          maxLength: 100
        },
        age: {
          bsonType: "int",
          minimum: 0,
          maximum: 150
        },
        role: {
          bsonType: "string",
          enum: ["user", "admin", "moderator"]
        },
        createdAt: {
          bsonType: "date"
        }
      },
      additionalProperties: false
    }
  },
  validationLevel: "strict",
  validationAction: "error"
})

A successful insert requires email, name, and createdAt to be present. An insert missing email returns a WriteError with the message "Document failed validation". The additionalProperties: false line means any field not listed in properties — including a mistyped field name — also fails.

To add a validator to an existing collection, use collMod:

db.runCommand({
  collMod: "users",
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["email", "name"],
      properties: {
        email: { bsonType: "string" },
        name:  { bsonType: "string" }
      }
    }
  },
  validationLevel: "moderate",
  validationAction: "warn"
})

bsonType vs type

Standard JSON Schema uses the type keyword with values like "string", "number", "boolean", and "null". MongoDB extends this with bsonType, which maps to BSON's richer type system. You can use either keyword in a MongoDB validator, but bsonType is the recommended choice because it gives you precision over numeric storage types and access to MongoDB-native types like objectId and date.

bsonType valueBSON typeJS / driver equivalent
"string"UTF-8 stringstring
"int"32-bit integerInt32 / number
"long"64-bit integerLong / BigInt
"double"64-bit floatnumber
"decimal"128-bit decimalDecimal128
"bool"Booleanboolean
"date"UTC datetimeDate
"objectId"ObjectIdObjectId
"array"ArrayArray
"object"Embedded documentobject
"binData"Binary dataBinary / Buffer
"null"Nullnull

To accept multiple types (like a field that can be either a string or null), pass an array: bsonType: ["string", "null"]. This is the bsonType equivalent of JSON Schema's type: ["string", "null"].

// Accept string or null (optional field that must be a string when present)
{
  bsonType: ["string", "null"],
  maxLength: 200
}

// Accept int or long (either 32-bit or 64-bit integer)
{
  bsonType: ["int", "long"],
  minimum: 0
}

// Reject: using type instead of bsonType for MongoDB dates
// type: "string" would reject BSON date objects
// bsonType: "date" is correct for MongoDB Date fields
{
  bsonType: "date"   // accepts new Date() — BSON UTC datetime
}

Required Fields and additionalProperties

The required array lists field names that must be present in every document. The additionalProperties keyword controls whether fields not listed in properties are allowed. Together they give you precise control over document shape.

db.createCollection("orders", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["_id", "userId", "items", "total", "status", "createdAt"],
      additionalProperties: false,
      properties: {
        _id:       { bsonType: "objectId" },
        userId:    { bsonType: "objectId" },
        items: {
          bsonType: "array",
          minItems: 1,
          items: {
            bsonType: "object",
            required: ["productId", "quantity", "price"],
            additionalProperties: false,
            properties: {
              productId: { bsonType: "objectId" },
              quantity:  { bsonType: "int", minimum: 1 },
              price:     { bsonType: "decimal", minimum: 0 }
            }
          }
        },
        total:     { bsonType: "decimal", minimum: 0 },
        status:    {
          bsonType: "string",
          enum: ["pending", "paid", "shipped", "delivered", "cancelled"]
        },
        createdAt: { bsonType: "date" },
        updatedAt: { bsonType: "date" }
      }
    }
  }
})

When additionalProperties: false is set at the top level, you must list _id in properties — MongoDB always adds _id before validation, so omitting it causes every insert to fail. The same rule applies to embedded documents: the items array entries also use additionalProperties: false with their own _id-free schema (embedded documents do not automatically get _id).

Updating Existing Collections

Use the collMod command to add or replace a validator on an existing collection. The collection does not need to be empty — the validator takes effect for writes after the command runs. Existing documents that violate the new schema are not automatically rejected until they are next updated (under "strict" level) or never (under "moderate" level).

// Step 1: Add validator in "warn" mode to find violations without breaking writes
db.runCommand({
  collMod: "products",
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["name", "price", "sku"],
      properties: {
        name:  { bsonType: "string", minLength: 1 },
        price: { bsonType: "decimal", minimum: 0 },
        sku:   { bsonType: "string", pattern: "^[A-Z]{2}-[0-9]{4}$" }
      }
    }
  },
  validationLevel: "moderate",
  validationAction: "warn"
})

// Step 2: Find documents that fail the schema
db.products.find({
  $nor: [{
    $jsonSchema: {
      bsonType: "object",
      required: ["name", "price", "sku"],
      properties: {
        name:  { bsonType: "string", minLength: 1 },
        price: { bsonType: "decimal", minimum: 0 },
        sku:   { bsonType: "string", pattern: "^[A-Z]{2}-[0-9]{4}$" }
      }
    }
  }]
})

// Step 3: After fixing legacy documents, switch to strict enforcement
db.runCommand({
  collMod: "products",
  validationLevel: "strict",
  validationAction: "error"
})

To remove a validator entirely, pass an empty object: db.runCommand({ collMod: "products", validator: {} }). To inspect the current validator, run db.getCollectionInfos({ name: "products" }) and check the options.validator field in the output.

Validation Levels and Actions

MongoDB provides two independent settings that control how validation is applied: validationLevel (which documents are checked) and validationAction (what happens when a document fails).

SettingValueBehavior
validationLevel"strict" (default)Validates all inserts and all updates, regardless of whether the document already exists
validationLevel"moderate"Validates inserts and updates to documents that already pass the validator; skips existing non-conforming documents
validationLevel"off"Disables validation entirely; the validator definition is retained but not applied
validationAction"error" (default)Rejects violating writes with a WriteError; the document is not stored
validationAction"warn"Allows the write to succeed but logs the violation to the MongoDB log

The recommended migration sequence is: moderate + warn first, then strict + error once legacy data is cleaned up. Never start with strict + error on a collection with existing data unless you have audited every document against the new schema.

Querying with $jsonSchema

Beyond collection validation, $jsonSchema is a first-class query operator. You can use it inside find() and aggregate() to filter documents by schema conformance — useful for auditing, data quality checks, and pre-migration analysis.

// Find all documents that MATCH a schema
db.users.find({
  $jsonSchema: {
    bsonType: "object",
    required: ["email", "age"],
    properties: {
      email: { bsonType: "string" },
      age:   { bsonType: "int", minimum: 18 }
    }
  }
})

// Find all documents that FAIL a schema (pre-migration audit)
db.users.find({
  $nor: [{
    $jsonSchema: {
      bsonType: "object",
      required: ["email", "name"],
      properties: {
        email: { bsonType: "string" },
        name:  { bsonType: "string" }
      }
    }
  }]
})

// Count violations
db.users.countDocuments({
  $nor: [{
    $jsonSchema: {
      required: ["email"]
    }
  }]
})

// Use $jsonSchema in an aggregation pipeline
db.users.aggregate([
  {
    $match: {
      $jsonSchema: {
        required: ["email", "name"],
        properties: {
          email: { bsonType: "string" },
          name:  { bsonType: "string", minLength: 1 }
        }
      }
    }
  },
  { $group: { _id: "$role", count: { $sum: 1 } } }
])

The $nor + $jsonSchema pattern is the fastest way to count and inspect non-conforming documents before you add a strict collection validator. Run it, fix the documents it returns, then re-run until the count is zero.

MongoDB vs Standard JSON Schema

MongoDB's $jsonSchema is based on JSON Schema Draft 4 with BSON extensions. It covers the most common validation keywords but omits several features available in Draft 7 and Draft 2020-12. Knowing the differences prevents surprises when porting schemas between MongoDB and application-layer validators like Ajv.

FeatureStandard JSON SchemaMongoDB $jsonSchema
requiredSupportedSupported
propertiesSupportedSupported
additionalPropertiesSupportedSupported
enumSupportedSupported
patternSupportedSupported
allOf / anyOf / oneOf / notSupportedSupported
bsonTypeNot applicableSupported (MongoDB extension)
format (date-time, email)Supported (Draft 4+)Not supported
$ref / $defsSupported (Draft 4+)Not supported
if / then / elseSupported (Draft 7+)Not supported
unevaluatedPropertiesSupported (Draft 2019-09+)Not supported

For validation features MongoDB does not support — such as if/then/else conditional logic or $ref-based schema composition — validate at the application layer using Ajv before calling insertOne() or updateOne(). Layer both: application-layer validation for rich error messages and complex logic, MongoDB validation as a final safety net at the database level.

Key Terms

$jsonSchema
A MongoDB operator used inside a collection validator or as a query filter. When used as a validator, MongoDB evaluates the schema against every document written to the collection. When used as a query operator inside find() or aggregate(), it filters documents to those that conform (or, with $nor, those that do not conform) to the specified schema. MongoDB's $jsonSchema is based on JSON Schema Draft 4 with BSON-specific extensions.
bsonType
A MongoDB extension to JSON Schema that specifies the expected BSON type of a field. Unlike the standard type keyword (which uses JSON type names), bsonType uses BSON type names such as "objectId", "date", "int", "long", "decimal", and "binData". Multiple types are accepted by passing an array: bsonType: ["string", "null"]. Using bsonType rather than type is the recommended practice for MongoDB validators because it accurately reflects MongoDB's storage model.
validationLevel
A collection-level setting that controls which documents are subject to validator evaluation. "strict" (the default) validates all inserts and all updates. "moderate" validates inserts and updates to documents that already pass the current validator, but skips updates to documents that were written before the validator existed or that were created while the validator was in "off" mode. "off" disables validation without removing the validator definition. Set via db.createCollection() or db.runCommand({ collMod: "..." }).
validationAction
A collection-level setting that controls what MongoDB does when a document fails validator evaluation. "error" (the default) rejects the write and returns a WriteError to the client; the document is not stored. "warn" allows the write to proceed but records the violation in the MongoDB server log, including which schema rules were violated. The "warn" mode is used during schema migration to detect violations in production traffic without breaking the application.
Collection validator
A database-level rule attached to a MongoDB collection that MongoDB evaluates on every write operation (insert, update, and replace). The validator is defined when the collection is created or added later with collMod. It contains a query expression — typically a $jsonSchema object — that every document must satisfy. The validator is stored as part of the collection's metadata and persists across server restarts. It runs inside the MongoDB storage engine, before any application code sees the result, making it a reliable safety net independent of which driver or ORM is used to write data.

FAQ

How do I add JSON Schema validation to a MongoDB collection?

Pass a validator option to db.createCollection() with a $jsonSchema object defining your schema. For an existing collection, use db.runCommand({ collMod: "collectionName", validator: { $jsonSchema: { ... } } }). MongoDB will then validate every new write against the schema. To see the current validator, run db.getCollectionInfos({ name: "collectionName" }) and inspect options.validator in the output.

What is bsonType and how does it differ from type in MongoDB JSON Schema?

bsonType uses MongoDB's BSON type names ("string", "int", "long", "double", "decimal", "bool", "date", "objectId", "array", "object"). The standard JSON Schema type keyword uses JSON type names ("string", "number", "boolean", "array", "object", "null"). The critical difference is numeric precision: type: "number" matches any MongoDB numeric type, while bsonType: "int" only matches 32-bit integers and bsonType: "double" only matches 64-bit floats. Use bsonType for MongoDB validators — it maps accurately to what MongoDB actually stores.

How do I make fields required in MongoDB JSON Schema?

Add a required array at the same schema level as properties, listing the field names that must be present. MongoDB checks for field existence on every insert and update. The required keyword does not validate the field's value — combine it with a properties entry to enforce both presence and type. For embedded documents, add a nested required array inside the embedded document's schema.

What does additionalProperties: false do in MongoDB JSON Schema?

It rejects documents containing any field not listed in the properties map. Because MongoDB automatically adds _id to every inserted document, you must include _id in your properties definition when using additionalProperties: false — otherwise all inserts fail. The same rule applies to embedded documents only if they also use additionalProperties: false; embedded documents do not receive an automatic _id from MongoDB.

What is the difference between validationLevel strict and moderate in MongoDB?

"strict" (default) validates every insert and every update, including updates to documents that were written before the validator existed. "moderate" validates inserts and updates to documents that already conform to the validator, but allows updates to pre-existing non-conforming documents to succeed without validation. Use "moderate" when rolling out a new schema to a collection that already contains documents — it prevents breaking existing update paths while still enforcing the schema for new data.

What does validationAction: "warn" do in MongoDB?

It lets violating writes succeed while recording the violation to the MongoDB server log. The log entry includes the document and the specific schema rules that failed. This mode is for schema migration: deploy the validator in warn mode, let production traffic run for a period, then query the logs to find violating paths and documents. Once you have fixed all violations, switch to validationAction: "error" to enforce the schema strictly.

Can I use $jsonSchema in MongoDB queries to find non-conforming documents?

Yes. $jsonSchema is a query operator that works in find(), aggregate($match), and countDocuments(). To find documents that violate a schema, wrap the schema in $nor: db.users.find({ $nor: [{ $jsonSchema: { ... } }] }). This is the standard pre-migration audit pattern — run it to count and inspect violations before adding a strict collection validator.

How does MongoDB JSON Schema validation compare to standard JSON Schema?

MongoDB's $jsonSchema is based on JSON Schema Draft 4 and supports required, properties, additionalProperties, enum, pattern, minimum, maximum, minLength, maxLength, items, allOf, anyOf, oneOf, and not. It adds bsonType for BSON types. It does not support $ref, $defs, format keywords (email, date-time), if/then/else (Draft 7), or unevaluatedProperties (Draft 2019-09+). For features MongoDB lacks, validate at the application layer with Ajv before writing to the database.

Further reading and primary sources