JSPM

nox-mongo-driver

1.4.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 17
  • Score
    100M100P100Q78962F
  • License MIT

A fast runtime schema-driven sanitizer & validator for dynamic schemas — alternative to Mongoose validation for multi-tenant apps.

Package Exports

  • nox-mongo-driver
  • nox-mongo-driver/dist/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (nox-mongo-driver) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

nox-mongo-driver

A fast, async-first, schema-driven payload sanitizer, validator & composable rule engine for Node.js and MongoDB applications. Built as a lightweight alternative to Mongoose validation — ideal for multi-tenant systems where schemas are defined dynamically at runtime.

npm version License: MIT

Interactive Docs & Playground: nox-sanitizer.nowonlinetech.in

Installation

npm install nox-mongo-driver

Quick Start

import { sanitize } from "nox-mongo-driver";

const schema = {
  name: { type: "String", required: true, trim: true },
  age: { type: "Number", validation: { _between: [18, 65] } },
  email: {
    type: "String",
    validation: {
      _and: [
        { _regex: "^[^@]+@[^@]+\\.[a-zA-Z]{2,}$" },
        { _not: { _contains: "spam" } },
      ],
    },
  },
};

const { value, errors } = await sanitize(
  { name: "  Sahil ", age: "25", email: "sahil@test.com" },
  schema,
  { mode: "strict" }
);

// value → { name: "Sahil", age: 25, email: "sahil@test.com" }
// errors → []

Try this example live in the Playground

Features

Schema Sanitizer

  • Three processing modes — strict, transform, validate (docs)
  • Type coercion — strings to numbers, booleans, dates, ObjectIds
  • Rule validation — required, min/max, enum, regex, minLength/maxLength
  • Nested objects & arrays — inline and explicit styles
  • Bare Object / Array types — type-only validation without nested schema
  • Recursive schemas — self-referencing via lazy functions
  • Custom validators — sync and async with context injection
  • Setter functions — transform values before coercion
  • Immutable fields — prevent updates on specific fields
  • Auto timestamps — createdAt / updatedAt with customizable field names
  • Partial mode — skip required checks for PATCH / update operations
  • Bulk sanitization — process arrays of payloads with concurrency control
  • Cleanup options — strip nulls, empty objects, empty arrays, undefined
  • Unknown field handling — strip, keep, or error
  • Prototype pollution protection — blocks __proto__, constructor, prototype
  • i18n support — custom error messages per language
  • Stop on first error — bail early for performance

Composable Rule Engine

  • 20 comparison operators_eq, _neq, _lt, _lte, _gt, _gte, _in, _nin, _contains, _contains_i, _starts_with, _starts_with_i, _ends_with, _ends_with_i, _is_null, _regex, _between, _length, _empty, _not_empty (operator reference)
  • 3 logical operators_and, _or, _not (unlimited nesting)
  • Cross-field references_ref to compare against sibling, parent, or root fields
  • Conditional validationwhen to apply rules based on another field's value
  • Per-operator error codes — each operator produces its own specific error code and message (error codes)
  • i18n per operator — translate each operator message independently

Documentation

Full interactive documentation with live playground, operator reference, schema options, and error codes:

nox-sanitizer.nowonlinetech.in

  • Playground — edit schemas, payloads, and options live with 34 preset examples
  • Operator Reference — all 23 operators with descriptions, examples, and error codes
  • Features — detailed docs for every feature with code examples
  • Schema Options — searchable table of all 21 schema field options
  • Error Codes — all 37 error codes with triggers and default messages

Modes

Mode Type Validation Rule Validation Transformation
strict Yes Yes Yes
transform Yes No Yes
validate Yes Yes No
await sanitize(payload, schema, { mode: "transform" });

Schema Field Options

Full searchable reference: Schema Options

{
  type: "String" | "Number" | "Boolean" | "Date" | "ObjectId" | "Object" | "Array" | "Mixed",
  required: boolean,
  default: any | (() => any),
  enum: any[],
  match: RegExp,
  min: number,
  max: number,
  minLength: number,
  maxLength: number,
  trim: boolean,
  lowercase: boolean,
  uppercase: boolean,
  immutable: boolean,
  set: (value, context) => any,
  validate: (value, context) => boolean | string,
  asyncValidate: async (value, context) => boolean | string,
  validation: { /* composable rule engine */ },
  when: { field, is, then, otherwise },
}

Composable Rule Engine (validation key)

Interactive examples: Playground > Operators

The validation key on any schema field accepts a composable rule tree. Rules can be nested with _and, _or, and _not.

Comparison Operators

Full reference with error codes: Operator Reference

Operator Description Example
_eq Equals { _eq: "active" }
_neq Not equals { _neq: "deleted" }
_lt Less than { _lt: 100 }
_lte Less than or equal { _lte: 65 }
_gt Greater than { _gt: 0 }
_gte Greater than or equal { _gte: 18 }
_in Value in array { _in: ["admin", "editor"] }
_nin Value not in array { _nin: ["banned"] }
_contains String contains { _contains: "hello" }
_contains_i Contains (case insensitive) { _contains_i: "Hello" }
_starts_with Starts with { _starts_with: "admin" }
_starts_with_i Starts with (case insensitive) { _starts_with_i: "Admin" }
_ends_with Ends with { _ends_with: ".pdf" }
_ends_with_i Ends with (case insensitive) { _ends_with_i: ".PDF" }
_is_null Is null/undefined { _is_null: true }
_regex Regex match { _regex: "^[A-Z]" } or { _regex: { pattern: "^[a-z]", flags: "i" } }
_between Value in range (inclusive) { _between: [18, 65] }
_length Check string/array length { _length: { _gte: 3, _lte: 50 } }
_empty Is empty (string/array/object/null) { _empty: true }
_not_empty Is not empty { _not_empty: true }

Logical Operators

// _and — all conditions must pass
validation: {
  _and: [
    { _gte: 18 },
    { _lte: 65 }
  ]
}

// _or — at least one must pass
validation: {
  _or: [
    { _eq: "active" },
    { _eq: "pending" }
  ]
}

// _not — negation
validation: {
  _not: { _contains: "admin" }
}

// deep nesting
validation: {
  _and: [
    { _starts_with: "user_" },
    { _not: { _in: ["user_banned", "user_suspended"] } },
    { _or: [
      { _ends_with: "_admin" },
      { _ends_with: "_editor" }
    ]}
  ]
}

Cross-Field References (_ref)

Try it live: Playground > Cross-Field

Compare a field's value against another field anywhere in the payload.

const schema = {
  password: { type: "String", required: true },
  confirmPassword: {
    type: "String",
    validation: { _eq: { _ref: "password" } },
  },
  startDate: { type: "Date" },
  endDate: {
    type: "Date",
    validation: { _gt: { _ref: "startDate" } },
  },
};

Path Resolution

Syntax Resolves To
{ _ref: "fieldName" } Sibling field in same parent object
{ _ref: "$root.path.to.field" } Absolute path from root payload
{ _ref: "$parent.field" } One level up
{ _ref: "$parent.$parent.field" } Two levels up

Works at Any Nesting Depth

const schema = {
  maxQuantity: { type: "Number" },
  orders: [{
    items: [{
      quantity: {
        type: "Number",
        validation: { _lte: { _ref: "$root.maxQuantity" } },
      },
    }],
  }],
};

Conditional Validation (when)

Try it live: Playground > Conditional

Apply different validation rules based on another field's value.

const schema = {
  type: { type: "String", enum: ["individual", "company"] },
  companyName: {
    type: "String",
    when: {
      field: "type",
      is: { _eq: "company" },
      then: { required: true },
      otherwise: { required: false },
    },
  },
};

when with Validation Rules

const schema = {
  country: { type: "String" },
  taxId: {
    type: "String",
    when: {
      field: "country",
      is: { _eq: "NL" },
      then: { required: true, validation: { _starts_with: "NL" } },
    },
  },
};

when with $root Reference (Inside Arrays)

const schema = {
  currency: { type: "String" },
  items: [{
    price: {
      type: "Number",
      when: {
        field: "$root.currency",
        is: { _eq: "EUR" },
        then: { validation: { _gte: 0.01 } },
      },
    },
  }],
};

Type Coercion

In strict or transform mode, values are automatically coerced to the declared type:

const schema = {
  age: { type: "Number" },
  isActive: { type: "Boolean" },
  createdAt: { type: "Date" },
};

// Input:  { age: "25", isActive: "true", createdAt: "2024-01-01" }
// Output: { age: 25, isActive: true, createdAt: Date("2024-01-01") }

Nested Objects

// Inline nested
{ profile: { age: { type: "Number" }, email: { type: "String" } } }

// Explicit with type
{ profile: { type: "Object", schema: { age: { type: "Number" } } } }

// Bare (type-only, pass-through)
{ metadata: { type: "Object" } }

Arrays

// Inline array of objects
items: [{ name: { type: "String", required: true } }]

// Explicit with bounds
items: { type: "Array", minItems: 1, maxItems: 50, schema: { name: { type: "String" } } }

// Bare (type-only)
{ tags: { type: "Array" } }

Recursive Schemas (Lazy)

const MenuItemSchema = () => ({
  id: { type: "String", required: true },
  children: [MenuItemSchema],
});

const schema = { modules: [MenuItemSchema] };

Setter (set)

{
  slug: {
    type: "String",
    set: (value) => value.toLowerCase().replace(/\s+/g, "-"),
  }
}
// Input: "My Blog Post" → Output: "my-blog-post"

Custom Validation

// Sync
username: {
  type: "String",
  validate: (value) => value.length >= 3 || "Too short"
}

// Async
email: {
  type: "String",
  asyncValidate: async (value, context) => {
    const exists = await context.db.findUser(value);
    return !exists || "Email already exists";
  }
}

Immutable Fields

{ email: { type: "String", immutable: true } }
// On create (partial: false) — accepted
// On update (partial: true) — rejected with IMMUTABLE_FIELD

Auto Timestamps

await sanitize(payload, schema, { timestamps: true });
// Create → { createdAt: Date, updatedAt: Date }
// Update (partial: true) → { updatedAt: Date }

Partial Mode (For Updates)

await sanitize(payload, schema, { partial: true });
// Skips required + default for missing fields

Bulk Sanitization

import { sanitizeBulk } from "nox-mongo-driver";
const result = await sanitizeBulk(payloads, schema, { concurrency: 5 });

Unknown Field Handling

unknownFields: "strip"   // default — removes unknown fields
unknownFields: "keep"    // preserves unknown fields
unknownFields: "error"   // adds error for each unknown field

Cleanup Options

await sanitize(payload, schema, {
  removeNull: true,
  removeUndefined: true,
  removeEmptyObjects: true,
  removeEmptyArrays: true,
});

Stop On First Error

await sanitize(payload, schema, { stopOnFirstError: true });

Error Format

Full error code reference: Error Codes

{
  path: "profile.age",
  code: "VALIDATION_GTE",
  message: "Must be greater than or equal to 18",
  meta: { operator: "_gte", expected: 18, received: 15 }
}

Error Codes

Schema rules: FIELD_REQUIRED · INVALID_TYPE · ENUM_MISMATCH · REGEX_MISMATCH · MIN_VIOLATION · MAX_VIOLATION · MIN_LENGTH_VIOLATION · MAX_LENGTH_VIOLATION · MIN_ITEMS_VIOLATION · MAX_ITEMS_VIOLATION · EXPECTED_ARRAY · EXPECTED_OBJECT · UNKNOWN_FIELD · IMMUTABLE_FIELD · CUSTOM_VALIDATION · CUSTOM_ASYNC_VALIDATION

Validation operators: VALIDATION_EQ · VALIDATION_NEQ · VALIDATION_LT · VALIDATION_LTE · VALIDATION_GT · VALIDATION_GTE · VALIDATION_IN · VALIDATION_NIN · VALIDATION_CONTAINS · VALIDATION_CONTAINS_I · VALIDATION_STARTS_WITH · VALIDATION_STARTS_WITH_I · VALIDATION_ENDS_WITH · VALIDATION_ENDS_WITH_I · VALIDATION_IS_NULL · VALIDATION_REGEX · VALIDATION_BETWEEN · VALIDATION_LENGTH · VALIDATION_EMPTY · VALIDATION_NOT_EMPTY · VALIDATION_NOT


i18n / Custom Messages

await sanitize(payload, schema, {
  language: "nl",
  messages: {
    nl: {
      FIELD_REQUIRED: "Veld is verplicht",
      VALIDATION_CONTAINS: (expected) => `Moet "${expected}" bevatten`,
      VALIDATION_GTE: (expected) => `Moet minimaal ${expected} zijn`,
    },
  },
});

Conflict Resolution

When a validation key is present, conflicting schema rules are automatically skipped:

Skipped Schema Rule Use Instead
enum _in / _nin
match (regex) _regex, _contains, _starts_with, _ends_with
min / max _gt, _gte, _lt, _lte, _between

Non-conflicting rules always run: required, minLength, maxLength, validate, asyncValidate.


// Create — full validation + coercion
await sanitize(payload, schema, { mode: "strict" });

// Update — skip required, reject immutable
await sanitize(payload, schema, { mode: "strict", partial: true });

// Data transformation layer — coerce types only
await sanitize(payload, schema, { mode: "transform" });

// Validation gate — check without mutating
await sanitize(payload, schema, { mode: "validate" });

Security

Automatically strips prototype pollution vectors (__proto__, constructor, prototype) from both schema keys and input data when unknownFields is set to keep.


License

MIT — NOX Team