All questions

Schema Validator III

Premium

Schema Validator III

Build validate(schema, value) — a validator that checks a runtime value against a schema, a plain-object description of the shape that value should have. This is the job a library like Zod or Joi does for you: an API endpoint receives a JSON request body, or a tool loads a config file, and before trusting that data you confirm every field has the right type, every required field is present, and nested objects and arrays match their declared shapes. Your validator handles primitives (string, number, boolean), nested object shapes, array shapes, and fields marked optional. Crucially, it does not stop at the first problem — it walks the entire value and reports every error, each tagged with a path saying exactly where it occurred.

Signature

// A Schema is a plain object describing an expected shape:
//   { type: 'string' }                                  — a primitive
//   { type: 'number' }
//   { type: 'boolean' }
//   { type: 'object', fields: { <key>: Schema, ... } }  — nested object
//   { type: 'array', items: Schema }                    — homogeneous array
// Any field schema may also carry `optional: true`.
//
// returns: { valid: boolean, errors: Array<{ path: string, message: string }> }
//   `valid` is true exactly when `errors` is empty.
//   `path` locates the failure: '' for the root value, 'user.age' for a
//   nested field, 'tags[2]' for an array element, 'tags[2].label' combined.
function validate(schema, value): { valid: boolean, errors: { path: string, message: string }[] };

Examples

// A valid nested object → no errors.
const schema = {
  type: 'object',
  fields: {
    name: { type: 'string' },
    age: { type: 'number' },
    tags: { type: 'array', items: { type: 'string' } },
  },
};
validate(schema, { name: 'Ada', age: 36, tags: ['math', 'code'] });
// → { valid: true, errors: [] }
// An invalid value collects MULTIPLE errors in one pass, each with a path.
validate(schema, { name: 42, age: 'old', tags: ['math', 7] });
// → {
//     valid: false,
//     errors: [
//       { path: 'name',    message: 'Expected string, got number' },
//       { path: 'age',     message: 'Expected number, got string' },
//       { path: 'tags[1]', message: 'Expected string, got number' },
//     ],
//   }

Notes

  • Supported types are exactly string, number, boolean, object, and array. Primitive types compare with typeof; object and array recurse into fields and items respectively.
  • Required vs optional. A field is required unless its schema has optional: true. A missing required field is an error; a missing optional field is valid. A required field whose value is explicitly undefined is treated the same as missing — an error.
  • All errors are collected. Do not fail fast. One call must surface every mismatch in the whole value, so the caller can show a complete list rather than fixing one problem only to discover the next.
  • Extra keys are flagged. A key present in the value but absent from the object schema's fields produces an error at that key's path. (Some validators ignore unknown keys; this one rejects them.)
  • Path format. Object fields join with a dot (user.age); array elements use a bracketed index (tags[2]); the two combine left to right (tags[2].label). The root value's path is the empty string ''.
  • null is not an object here. typeof null === 'object' in JavaScript, but a null value where an object schema is expected is an error, not a valid empty object.

Unlock the solution & editor

  • Runnable editor + tests

    Solve in the browser with instant Jest feedback.

  • Detailed solutions

    Walkthroughs, edge cases, and complexity notes.

  • Multi-framework variants

    React, Vue, Vanilla, Angular — same question, different stacks.

Upgrade to Premium