skip to content

Schema Validation

How a 'schemaless' database still enforces shape when you want it to. A common follow-up to 'flexible schema sounds dangerous — how do you stop garbage getting in?'

part ofMongoDBoverview, primer and where to startread it →
on this pageshow

questions

5

How do you make a MongoDB collection reject documents that lack required fields?

level: juniorimportance: must knowfreq 55%

answer

  1. MongoDB enforces nothing by default
  2. the rule lives in the collection's options
  3. a query expression the document must match
  4. createCollection takes a validator option
  5. $jsonSchema with required and bsonType

basics

~20 s

Attach a validator to the collection — normally a $jsonSchema object listing required fields and their bsonType — using createCollection or collMod. MongoDB then checks every insert and update against it and rejects writes that break the rules.

solid answer

~40 s

MongoDB stores whatever BSON you hand it unless the collection carries a **validator**. A validator is a query expression kept in the collection's options; the usual form is `$jsonSchema`, where `required` lists the field names a document must contain and `properties` constrains each field with keywords like `bsonType`, `enum`, `minimum` or `maxLength`. You attach it with `db.createCollection("orders", { validator: { $jsonSchema: { ... } } })`, or add or replace it later on an existing collection with the `collMod` command. From then on every insert and every update is checked; by default (`validationAction: "error"`) a document that fails is rejected with a write error naming the rule it broke. Documents already stored are untouched — validation runs on writes, not retroactively.

go deeper

for a junior

Know that a MongoDB collection accepts anything until you attach a validator, and be able to write a small $jsonSchema with required and properties by hand.

for a middle

Explain where the validator lives, which writes it is evaluated against, and why bsonType exists alongside type. Be ready to add one to an existing collection with collMod.

for a senior

Show judgment about how strict to be: additionalProperties: false versus an open document, which fields genuinely must be required, and how a validator interacts with rolling deploys that add fields.

for a principal

Own the policy question of which collections carry validators at all, who reviews changes to them, and how bypassDocumentValidation in restore and migration tooling fits into that guarantee.

## MongoDB checks nothing until you ask it to A MongoDB collection has no declared shape. Two documents in the same collection can have completely different fields and completely different types for the same field name. That is deliberate, but it means nothing stops a buggy service from writing `{ total: "12.50" }` next to `{ total: 12.50 }`, or omitting `customerId` entirely. Server-side **document validation** is the mechanism that closes that hole for the collections where you want it closed. ## What a validator actually is A validator is a query expression stored in the collection's options. Any document a write would produce must match that expression. Two forms exist: - A plain query predicate — `{ qty: { $gt: 0 } }` — which reads exactly like a `find()` filter. - A `$jsonSchema` expression, which is the form nearly everyone uses because it can describe a whole document tree in one place. You set it when creating the collection: ```js db.createCollection("orders", { validator: { $jsonSchema: { bsonType: "object", required: ["customerId", "status"], properties: { customerId: { bsonType: "objectId" }, status: { enum: ["new", "paid", "shipped"] } } } } }) ``` or add, replace or remove it later with `db.runCommand({ collMod: "orders", validator: { ... } })`. To see what a collection currently enforces, read `db.getCollectionInfos({ name: "orders" })` and look at `options.validator`, `options.validationLevel` and `options.validationAction`. ## The keywords you will actually use - `bsonType` — the type of the value, expressed with BSON aliases: `"object"`, `"string"`, `"int"`, `"long"`, `"double"`, `"decimal"`, `"bool"`, `"date"`, `"objectId"`, `"array"`, `"binData"`. It accepts an array of aliases when several types are legal. - `required` — an array of field names that must be present. It says nothing about their types; pair it with `properties`. - `properties` — a map from field name to a sub-schema. Sub-schemas nest, so an embedded object or an array element gets its own rules (`items` describes array elements). - `enum` — the closed list of allowed values for a field. - Value constraints — `minimum`/`maximum` for numbers, `minLength`/`maxLength`/`pattern` for strings, `minItems`/`maxItems`/`uniqueItems` for arrays. - `additionalProperties: false` — forbids any field not named in `properties`. Powerful, and the fastest way to break a rolling deploy that adds a field, so use it knowingly. - `description` and `title` — free text that shows up in the validation error, which is the difference between a debuggable rejection and a mystery. ## bsonType versus type Both exist. `type` is the standard JSON Schema keyword and only knows JSON type names — `"string"`, `"number"`, `"object"`, `"array"`, `"boolean"`, `"null"`. It cannot express an ObjectId, a Date, or the difference between a 32-bit int and a double, because JSON has no such notion. `bsonType` knows the full BSON type list. Use `bsonType` unless you have a specific reason not to; `bsonType: "objectId"` and `bsonType: "int"` are the two cases that bite people first. ## When the rules run Validation is a write-path check. Inserts are validated, and updates are validated against the resulting document — so an update that removes a required field is rejected just like an insert that never had it. Deletes are unaffected. Documents already in the collection when the validator is attached are not scanned, not rewritten and not flagged; they are only ever judged when something writes to them. Privileged callers, and tools such as restore utilities, can pass `bypassDocumentValidation: true` on a write to skip the check entirely, so a validator is a guardrail for your application code rather than an inviolable law. ## What it deliberately does not do A validator only accepts or rejects. It never fills in a default value — MongoDB's `$jsonSchema` does not support the JSON Schema `default` keyword — and it never coerces `"12.50"` into a number. It also cannot look at any document other than the one being written, so uniqueness is a unique index's job, not a validator's. Treat it as a shape gate at the door, and keep business invariants that span documents somewhere that can actually see them.

  • Does attaching a validator change or reject the documents already stored in the collection?
    No. Validation runs only on inserts and updates. Existing documents are not scanned, rewritten or removed, and they keep being readable. A stored document that breaks the new rules is only judged when something writes to it — and even then, `validationLevel: "moderate"` deliberately skips updates to documents that already fail.
  • When would you use bsonType instead of the standard type keyword?
    Almost always. `type` only understands JSON type names, so it cannot say "this must be an ObjectId", "this must be a Date", or "this must be a 32-bit int rather than a double". `bsonType` accepts the BSON aliases — `objectId`, `date`, `int`, `long`, `decimal`, `binData` — which is what documents actually contain.
  • Can a validator be something other than $jsonSchema?
    Yes. Any query expression works, for example `{ qty: { $gt: 0 } }`, or `$expr` to compare two fields of the same document. A few operators are disallowed inside validators: `$near`, `$nearSphere`, `$text` and `$where`. You can also combine forms with `$and` to sit a `$expr` rule alongside a `$jsonSchema` one.

saying these in an interview costs you the question

  • Says MongoDB cannot enforce structure at all because it is schemaless
  • Thinks adding a validator deletes or repairs existing non-conforming documents
  • Expects the validator to supply default values for missing fields
  • Believes required also constrains the field's type
  • Uses type: 'objectId', which is not a JSON Schema type name

context

open as a page

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

level: middleimportance: must knowfreq 52%

basics

~20 s

validationLevel decides which writes are checked. strict, the default, validates every insert and update. moderate validates inserts and updates to documents that already satisfy the validator, but skips updates to documents that currently fail it. off disables validation.

open as a page

What does validationAction warn do when a MongoDB write violates the collection validator?

level: middleimportance: should knowfreq 36%

basics

~10 s

With validationAction set to warn, MongoDB lets the write succeed and records the violation in the mongod log instead of failing it. The default, error, rejects the write and returns a document-failed-validation write error.

open as a page

What kinds of rules can a MongoDB $jsonSchema validator not enforce?

level: seniorimportance: should knowfreq 33%

basics

~20 s

A validator sees only the single document being written, so it cannot enforce uniqueness, cross-collection references, or anything about other documents. It also never supplies defaults or coerces types, and privileged writes can bypass it entirely.

open as a page

How would you add a $jsonSchema validator to a collection that already holds non-conforming documents?

level: seniorimportance: should knowfreq 44%

basics

~20 s

Roll it out in stages: attach the schema with collMod at validationLevel moderate and validationAction warn, measure the offenders by querying with $nor plus $jsonSchema, backfill them in batches while fixing the writers, then flip to strict and error.

open as a page