Skip to content

Validation

Mondel builds Zod schemas from your defineSchema definitions and runs them on write paths.

Modes

Configured on createClient:

typescript
createClient({
  schemas,
  validation: "strict", // default | "loose" | "off"
});
ModeBehaviorCreate defaults
strictInvalid data throws ZodErrorApplied
looseLogs a warning; operation continues with the original valuesApplied
offNo runtime validation (TypeScript still helps at compile time)Not applied

off is a full bypass: no validation and no defaults. If you only want to stop writes from throwing, use loose — it keeps writing the same document shape as strict.

What is validated

InputValidated
create / createMany bodyYes
Plain update object { name: "x" }Yes
$set / $setOnInsert, whole fieldsYes
$set with dot-notation keys ("profile.level")No
Other operators ($inc, $push, $unset, …)No
bulkWrite operationsNo
Read resultsNo

Dot-notation is not validated

The schema shape is keyed by field name, so a dot-notation key is an unknown key and passes through untouched. These two writes are not equivalent:

typescript
// validated — `profile` is a declared field
await db.users.updateOne(filter, { $set: { profile: { level: "oops" } } }); // throws

// not validated — "profile.level" is an unknown key
await db.users.updateOne(filter, { $set: { "profile.level": "oops" } });    // passes

Treat Mondel validation as a guardrail on whole-field writes, not a guarantee that everything reaching the collection matches the schema. For hard enforcement, install database-level validators (see below).

Defaults

Defaults are a property of the schema, not of a successful parse:

  • On create, field .default(...) values are applied at all nesting levels (nested objects and object items inside arrays) in strict and loose — including when a loose payload fails validation.
  • On update, defaults are not applied at any level — partial updates must not inject missing fields.
  • When validation: "off", defaults are not applied.

Non-primitive defaults (.default([]), .default({})) are cloned per document, so two documents never share the same array or object instance.

Unknown keys are kept (passthrough) so MongoDB open documents stay compatible.

Manual Zod access

typescript
import { zodSchema, zodCreateSchema, zodUpdateSchema, validate } from "mondel";

const createZ = zodCreateSchema(userSchema);
const result = validate(createZ, payload);
if (!result.success) {
  console.error(result.errors);
}

Use this for HTTP body validation before calling Mondel, or to validate legacy reads.

Performance

  • Validators are cached per collection proxy (built once).
  • Cost is on writes only.
  • Use validation: "off" only in trusted internal paths if profiling requires it.

Errors

typescript
import { ZodError } from "zod";

try {
  await db.users.create({ email: "not-an-email" });
} catch (e) {
  if (e instanceof ZodError) {
    // e.issues — field paths and messages
  }
}

Server-side validators

CLI push --apply-validators installs MongoDB $jsonSchema from the same field definitions. That is database-level enforcement, independent of the Node validation mode. See CLI.

Released under the MIT License. · llms.txt