Library Usage Guide

June 3, 2026 · View on GitHub

How to use z-schema as a dependency in other projects for JSON Schema validation. Supports JSON Schema draft-04, draft-06, draft-07, draft-2019-09, and draft-2020-12 (latest).

Installation

npm install z-schema

Import

// ESM / TypeScript
import ZSchema from 'z-schema';

// Or named import
import { ZSchema } from 'z-schema';

// CommonJS
const ZSchema = require('z-schema');

Creating a Validator

Always use ZSchema.create() — never new ZSchema().

const validator = ZSchema.create();

With Options

const validator = ZSchema.create({
  version: 'draft2020-12', // 'draft-04' | 'draft-06' | 'draft-07' | 'draft2019-09' | 'draft2020-12' | 'none' (default: 'draft2020-12')
  breakOnFirstError: false, // stop at first error (default: false)
  noEmptyStrings: true, // reject empty strings for type 'string' (default: false)
  noEmptyArrays: true, // reject empty arrays for type 'array' (default: false)
  noTypeless: true, // require 'type' in schemas (default: false)
  strictMode: false, // enable multiple strict checks at once (default: false)
  ignoreUnknownFormats: false, // suppress UNKNOWN_FORMAT errors for older drafts (default: false; modern drafts always ignore unknown formats)
  formatAssertions: null, // null=always assert, true=respect vocabulary (annotation-only for 2019-09/2020-12), false=annotation-only (default: null)
  ignoreUnresolvableReferences: false, // skip unresolvable $ref (default: false)
  maxRecursionDepth: 100, // max depth for internal traversal (prevents stack overflow on deep schemas/data)
});

See Options for the full list of options and detailed descriptions.

Validation Modes

Sync (throws on error) — default

const validator = ZSchema.create();

try {
  validator.validate(data, schema);
  // validation passed — returns true
} catch (err) {
  console.log(err.name); // 'ValidateError'
  console.log(err.message); // summary message
  console.log(err.details); // SchemaErrorDetail[]
}

Sync Safe (returns result object)

const validator = ZSchema.create({ safe: true });

const result = validator.validate(data, schema);
// result: { valid: boolean, err?: ValidateError }
if (!result.valid) {
  console.log(result.err?.details);
}

Or use the convenience method on a regular validator:

const validator = ZSchema.create();
const result = validator.validateSafe(data, schema);

Async (promise, throws on error)

Required when using async format validators.

const validator = ZSchema.create({ async: true });

try {
  await validator.validate(data, schema);
  // validation passed
} catch (err) {
  console.log(err.details);
}

Async Safe (promise, returns result object)

const validator = ZSchema.create({ async: true, safe: true });

const result = await validator.validate(data, schema);
if (!result.valid) {
  console.log(result.err?.details);
}

Schema Validation & Pre-compilation

Pre-validate and compile schemas at startup for better runtime performance:

const validator = ZSchema.create();

const schemas = [
  {
    id: 'person',
    type: 'object',
    properties: {
      name: { type: 'string' },
      age: { type: 'integer', minimum: 0 },
    },
    required: ['name'],
  },
  {
    id: 'team',
    type: 'object',
    properties: {
      members: { type: 'array', items: { $ref: 'person' } },
    },
  },
];

// Compile + validate all schemas (resolves cross-references)
try {
  validator.validateSchema(schemas);
} catch (err) {
  console.log('Schema validation failed:', err.details);
}

// Now validate data using the compiled schema ID
validator.validate({ name: 'Alice', age: 30 }, 'person');

Error Handling

ValidateError (thrown or returned) has:

  • .name'ValidateError'
  • .message — summary string
  • .details — array of SchemaErrorDetail:
interface SchemaErrorDetail {
  message: string; // "Expected type string but found type number"
  code: string; // "INVALID_TYPE"
  params: (string | number | Array<string | number>)[]; // ["string", "number"]
  path: string | Array<string | number>; // "#/age" or ["age"]
  schemaPath?: Array<string | number>; // ["properties", "age", "type"]
  title?: string; // schema title if present
  description?: string; // schema description if present
  inner?: SchemaErrorDetail[]; // sub-errors for anyOf/oneOf/not
  schemaId?: string; // schema id if present
  keyword?: string; // schema keyword that caused the error (e.g. "required", "type")
}

Error Codes

Common error codes (defined in src/errors.ts):

CodeMeaning
INVALID_TYPEValue type does not match schema type
INVALID_FORMATValue fails format validation
ENUM_MISMATCHValue not in enum list
ANY_OF_MISSINGNo schema in anyOf matched
ONE_OF_MISSINGNo schema in oneOf matched
ONE_OF_MULTIPLEMultiple schemas in oneOf matched
NOT_PASSEDData matched not schema
OBJECT_MISSING_REQUIRED_PROPERTYMissing required property
OBJECT_ADDITIONAL_PROPERTIESExtra property not allowed
ARRAY_LENGTH_SHORTArray below minItems
ARRAY_LENGTH_LONGArray above maxItems
MINIMUM / MAXIMUMNumber out of range
MIN_LENGTH / MAX_LENGTHString length out of range
PATTERNString does not match pattern
UNRESOLVABLE_REFERENCE$ref could not be resolved
CONSTValue does not match const (draft-06+)
CONTAINSArray has no item matching contains (draft-06+)
PROPERTY_NAMESProperty name fails propertyNames (draft-06+)
ARRAY_UNEVALUATED_ITEMSUnevaluated items not allowed (draft-2019-09+)
OBJECT_UNEVALUATED_PROPERTIESUnevaluated property not allowed (draft-2019-09+)

Filtering Errors

Use ValidateOptions to include or exclude specific error codes:

// Only report type errors
validator.validate(data, schema, { includeErrors: ['INVALID_TYPE'] });

// Suppress minimum-length errors
validator.validate(data, schema, { excludeErrors: ['MIN_LENGTH'] });

Validating Sub-schemas

Target a specific path within a schema:

validator.validate(data, schema, { schemaPath: '#/properties/address' });

Remote References

Important: Static remote references (ZSchema.setRemoteReference) are stored in a global cache shared across all ZSchema instances in the same process. This is intentional — it allows meta-schemas and common references to be registered once and reused everywhere. However, you must not register conflicting schemas under the same URI from different parts of your application, as the last write wins silently. Instance-level references (validator.setRemoteReference) are scoped to that instance and take precedence over global ones.

Manual

// Set a remote reference before validation
ZSchema.setRemoteReference('http://example.com/schemas/address.json', addressSchema);

// Or on an instance
validator.setRemoteReference('http://example.com/schemas/person.json', personSchema);

Automatic (Schema Reader)

Set a sync function that loads schemas on demand when a $ref is encountered:

import fs from 'node:fs';
import path from 'node:path';

ZSchema.setSchemaReader((uri) => {
  const filePath = path.resolve(__dirname, 'schemas', uri + '.json');
  return JSON.parse(fs.readFileSync(filePath, 'utf8'));
});

Diagnosing Missing References

const { valid, err } = validator.validateSafe(data, schema);
if (!valid && err) {
  const missing = validator.getMissingReferences(err); // all unresolved $refs
  const remote = validator.getMissingRemoteReferences(err); // only remote URIs
  // Fetch and register missing schemas, then retry
}

Custom Format Validators

Global (shared across all instances)

ZSchema.registerFormat('my-format', (value) => {
  if (typeof value !== 'string') return true; // skip non-strings
  return /^[A-Z]{3}-\d{4}$/.test(value);
});

Instance-scoped

const validator = ZSchema.create();
validator.registerFormat('my-format', (value) => {
  /* ... */
});

Async Format Validators

Return a Promise<boolean>. Requires using async validation mode.

validator.registerFormat('user-exists', async (value) => {
  if (typeof value !== 'number') return false;
  const user = await db.findUser(value);
  return user != null;
});

const asyncValidator = ZSchema.create({ async: true });
await asyncValidator.validate(data, schema);

Via Options (at creation time)

const validator = ZSchema.create({
  customFormats: {
    'my-format': (value) => typeof value === 'string' && value.length > 0,
  },
});

Draft Version Comparison

FeatureDraft-04Draft-06Draft-07Draft-2019-09Draft-2020-12 (latest)
Schema IDid$id$id$id, $anchor$id, $anchor
Exclusive min/maxexclusiveMinimum: booleanexclusiveMinimum: numberexclusiveMinimum: numberexclusiveMinimum: numberexclusiveMinimum: number
constN/ASupportedSupportedSupportedSupported
containsN/ASupportedSupported+ minContains/maxContains+ minContains/maxContains
propertyNamesN/ASupportedSupportedSupportedSupported
if/then/elseN/AN/ASupportedSupportedSupported
$defsN/AN/AN/ASupportedSupported
dependentRequiredN/AN/AN/ASupportedSupported
dependentSchemasN/AN/AN/ASupportedSupported
unevaluatedItemsN/AN/AN/ASupportedSupported
unevaluatedPropertiesN/AN/AN/ASupportedSupported
prefixItemsN/AN/AN/AN/ASupported (replaces array-form items)
$dynamicRefN/AN/AN/A$recursiveRef/$recursiveAnchor$dynamicRef/$dynamicAnchor
Boolean schemasN/Atrue (accept all) / false (reject all)SupportedSupportedSupported
Default versionDefault (version: 'draft2020-12')

To use a specific version:

const validator = ZSchema.create({ version: 'draft2020-12' });

Browser Usage (UMD)

<script src="node_modules/z-schema/umd/ZSchema.min.js"></script>
<script>
  var validator = ZSchema.create();
  try {
    validator.validate({ name: 'test' }, { type: 'object' });
  } catch (err) {
    console.log(err.details);
  }
</script>

CLI

npm install -g z-schema

# Validate a schema
z-schema mySchema.json

# Validate JSON against a schema
z-schema mySchema.json myData.json

# With strict mode
z-schema --strictMode mySchema.json myData.json

TypeScript Types

All types are exported from the package:

import type {
  JsonSchema, // Schema type (all supported drafts union)
  JsonSchemaCommon, // Properties common to all drafts
  JsonSchemaDraft4, // Draft-04 specific (id, boolean exclusiveMin/Max)
  JsonSchemaDraft6, // Draft-06 specific ($id, const, contains, propertyNames, examples)
  JsonSchemaDraft7, // Draft-07 (extends Draft-06 + if/then/else, content*)
  JsonSchemaDraft201909, // Draft 2019-09 (extends Draft-07 + $defs, $anchor, dependent*, unevaluated*, ...)
  JsonSchemaDraft202012, // Draft 2020-12 (extends 2019-09 + $dynamicAnchor/Ref, prefixItems)
  JsonSchemaVersion, // 'draft-04' | 'draft-06' | 'draft-07' | 'draft2019-09' | 'draft2020-12'
  JsonSchemaType, // 'string' | 'number' | 'integer' | 'boolean' | 'object' | 'array' | 'null'
  ZSchemaOptions, // Configuration options
  ValidateOptions, // Per-call options (schemaPath, includeErrors, excludeErrors)
  ValidateResponse, // { valid: boolean, err?: ValidateError }
  SchemaErrorDetail, // Individual error detail
  ErrorCode, // keyof typeof Errors
  FormatValidatorFn, // (input: unknown) => boolean | Promise<boolean>
  SchemaReader, // (uri: string) => JsonSchema
} from 'z-schema';

import { ValidateError } from 'z-schema';