JSON schema validator for JavaScript with comprehensive validation capabilities and support for both synchronous and asynchronous validation modes
Comprehensive error reporting system with detailed validation failure information and customizable error handling. z-schema provides rich error information to help debug validation failures and implement custom error handling logic.
Retrieve the most recent validation error as a structured Error object.
/**
* Get the most recent validation error
* @returns Error object with validation details or null if validation succeeded
*/
getLastError(): ZSchema.SchemaError | null;Usage Examples:
const ZSchema = require("z-schema");
const validator = new ZSchema();
const schema = {
type: "object",
properties: {
name: { type: "string", minLength: 5 },
age: { type: "number", minimum: 18 }
},
required: ["name", "age"]
};
const data = { name: "Jo", age: 16 };
const valid = validator.validate(data, schema);
if (!valid) {
const error = validator.getLastError();
console.log("Error name:", error.name);
// Output: "z-schema validation error"
console.log("Error message:", error.message);
// Output: Summary message about validation failure
console.log("Error details:", error.details);
// Output: Array of detailed error information
// Use as standard Error object
throw error;
}Retrieve detailed array of all validation errors from the most recent validation.
/**
* Get array of all validation errors from last validation
* @returns Array of error details or null if validation succeeded
*/
getLastErrors(): ZSchema.SchemaErrorDetail[] | null;Usage Examples:
const validator = new ZSchema();
const schema = {
type: "object",
properties: {
email: { type: "string", format: "email" },
age: { type: "number", minimum: 0, maximum: 120 },
tags: {
type: "array",
items: { type: "string" },
minItems: 1
}
},
required: ["email", "age"]
};
const invalidData = {
email: "not-an-email",
age: -5,
tags: []
};
const valid = validator.validate(invalidData, schema);
if (!valid) {
const errors = validator.getLastErrors();
errors.forEach((error, index) => {
console.log(`Error ${index + 1}:`);
console.log(` Code: ${error.code}`);
console.log(` Message: ${error.message}`);
console.log(` Path: ${error.path}`);
console.log(` Params:`, error.params);
if (error.inner && error.inner.length > 0) {
console.log(` Inner errors:`, error.inner.length);
}
});
/* Example output:
Error 1:
Code: INVALID_FORMAT
Message: Object didn't pass validation for format email: not-an-email
Path: #/email
Params: ["email", "not-an-email"]
Error 2:
Code: MINIMUM
Message: Value -5 is less than minimum 0
Path: #/age
Params: ["-5", "0"]
Error 3:
Code: ARRAY_LENGTH_SHORT
Message: Array is too short (0), minimum 1
Path: #/tags
Params: ["0", "1"]
*/
}Implement custom error handling with detailed error analysis.
// Error analysis helper functions
function analyzeValidationErrors(errors) {
const errorsByType = {};
const errorsByPath = {};
errors.forEach(error => {
// Group by error type
if (!errorsByType[error.code]) {
errorsByType[error.code] = [];
}
errorsByType[error.code].push(error);
// Group by path
if (!errorsByPath[error.path]) {
errorsByPath[error.path] = [];
}
errorsByPath[error.path].push(error);
});
return { errorsByType, errorsByPath };
}Usage Examples:
const validator = new ZSchema();
function validateWithCustomErrorHandling(data, schema) {
const valid = validator.validate(data, schema);
if (!valid) {
const errors = validator.getLastErrors();
const { errorsByType, errorsByPath } = analyzeValidationErrors(errors);
// Handle specific error types
if (errorsByType.INVALID_TYPE) {
console.log("Type validation errors found:");
errorsByType.INVALID_TYPE.forEach(error => {
console.log(` ${error.path}: expected ${error.params[0]}, got ${error.params[1]}`);
});
}
if (errorsByType.INVALID_FORMAT) {
console.log("Format validation errors found:");
errorsByType.INVALID_FORMAT.forEach(error => {
console.log(` ${error.path}: invalid ${error.params[0]} format`);
});
}
// Handle errors by field
Object.keys(errorsByPath).forEach(path => {
const fieldErrors = errorsByPath[path];
console.log(`Field ${path} has ${fieldErrors.length} error(s)`);
});
return {
valid: false,
errors: errors,
summary: {
totalErrors: errors.length,
errorTypes: Object.keys(errorsByType),
affectedFields: Object.keys(errorsByPath)
}
};
}
return { valid: true, errors: null, summary: null };
}
// Usage
const result = validateWithCustomErrorHandling(data, schema);
if (!result.valid) {
console.log("Validation summary:", result.summary);
}Implement custom validation logic with error reporting.
/**
* Custom validator function interface
* @param report - Report object for adding custom errors
* @param schema - Current schema being validated
* @param json - Current JSON data being validated
*/
interface CustomValidatorFunction {
(report: Report, schema: any, json: any): void;
}
interface Report {
/**
* Add custom error to validation report
* @param errorCode - Custom error code
* @param errorMessage - Error message template with {0}, {1} placeholders
* @param params - Array of parameters for message template
* @param subReports - Sub-schema reports (optional)
* @param schemaDescription - Schema description (optional)
*/
addCustomError(
errorCode: string,
errorMessage: string,
params: string[],
subReports?: any,
schemaDescription?: string
): void;
}Usage Examples:
// Custom validator for business logic
function businessRulesValidator(report, schema, json) {
// Custom validation: ensure unique properties
if (Array.isArray(schema.uniqueProperties)) {
const seenValues = [];
schema.uniqueProperties.forEach(prop => {
const value = json[prop];
if (typeof value !== "undefined") {
if (seenValues.indexOf(value) !== -1) {
report.addCustomError(
"NON_UNIQUE_PROPERTY_VALUE",
"Property '{0}' has non-unique value: {1}",
[prop, value.toString()],
null,
schema.description
);
}
seenValues.push(value);
}
});
}
// Custom validation: business date rules
if (schema.businessDateValidation && json.startDate && json.endDate) {
const start = new Date(json.startDate);
const end = new Date(json.endDate);
if (start >= end) {
report.addCustomError(
"INVALID_DATE_RANGE",
"Start date '{0}' must be before end date '{1}'",
[json.startDate, json.endDate],
null,
"Business rule: start date must precede end date"
);
}
const maxDuration = 365 * 24 * 60 * 60 * 1000; // 1 year in ms
if (end - start > maxDuration) {
report.addCustomError(
"DATE_RANGE_TOO_LONG",
"Date range cannot exceed 1 year: {0} to {1}",
[json.startDate, json.endDate]
);
}
}
}
// Use custom validator
const validator = new ZSchema({
customValidator: businessRulesValidator
});
const schema = {
type: "object",
properties: {
fromAccount: { type: "string" },
toAccount: { type: "string" },
startDate: { type: "string", format: "date" },
endDate: { type: "string", format: "date" }
},
uniqueProperties: ["fromAccount", "toAccount"],
businessDateValidation: true
};
// This will trigger custom validation errors
const invalidData = {
fromAccount: "12345",
toAccount: "12345", // Same as fromAccount - will trigger uniqueness error
startDate: "2023-12-01",
endDate: "2023-06-01" // Before startDate - will trigger date range error
};
const valid = validator.validate(invalidData, schema);
if (!valid) {
const errors = validator.getLastErrors();
errors.forEach(error => {
if (error.code.startsWith("NON_UNIQUE") || error.code.startsWith("INVALID_DATE")) {
console.log("Custom validation error:", error.message);
}
});
}interface SchemaError extends Error {
/** Always "z-schema validation error" */
name: string;
/** Summary error message */
message: string;
/** Detailed error information array */
details: SchemaErrorDetail[];
}
interface SchemaErrorDetail {
/** Descriptive error message */
message: string;
/** Error code identifier */
code: string;
/** Parameters used in error message */
params: string[];
/** JSON path to the error location */
path: string;
/** Schema description (if available) */
description: string;
/** Nested errors for complex validation failures */
inner: SchemaErrorDetail[];
}// Type validation errors
interface TypeErrors {
"INVALID_TYPE": "Expected type {0} but found type {1}";
"INVALID_FORMAT": "Object didn't pass validation for format {0}: {1}";
"ENUM_MISMATCH": "No enum match for: {0}";
"ENUM_CASE_MISMATCH": "Enum does not match case for: {0}";
}
// Composition validation errors
interface CompositionErrors {
"ANY_OF_MISSING": "Data does not match any schemas from 'anyOf'";
"ONE_OF_MISSING": "Data does not match any schemas from 'oneOf'";
"ONE_OF_MULTIPLE": "Data is valid against more than one schema from 'oneOf'";
"NOT_PASSED": "Data matches schema from 'not'";
}
// Array validation errors
interface ArrayErrors {
"ARRAY_LENGTH_SHORT": "Array is too short ({0}), minimum {1}";
"ARRAY_LENGTH_LONG": "Array is too long ({0}), maximum {1}";
"ARRAY_UNIQUE": "Array items are not unique (indexes {0} and {1})";
"ARRAY_ADDITIONAL_ITEMS": "Additional items not allowed";
}
// Numeric validation errors
interface NumericErrors {
"MULTIPLE_OF": "Value {0} is not a multiple of {1}";
"MINIMUM": "Value {0} is less than minimum {1}";
"MINIMUM_EXCLUSIVE": "Value {0} is equal or less than exclusive minimum {1}";
"MAXIMUM": "Value {0} is greater than maximum {1}";
"MAXIMUM_EXCLUSIVE": "Value {0} is equal or greater than exclusive maximum {1}";
}
// Object validation errors
interface ObjectErrors {
"OBJECT_PROPERTIES_MINIMUM": "Too few properties defined ({0}), minimum {1}";
"OBJECT_PROPERTIES_MAXIMUM": "Too many properties defined ({0}), maximum {1}";
"OBJECT_MISSING_REQUIRED_PROPERTY": "Missing required property: {0}";
"OBJECT_ADDITIONAL_PROPERTIES": "Additional properties not allowed: {0}";
"OBJECT_DEPENDENCY_KEY": "Dependency failed - key must exist: {0} (due to key: {1})";
}
// String validation errors
interface StringErrors {
"MIN_LENGTH": "String is too short ({0} chars), minimum {1}";
"MAX_LENGTH": "String is too long ({0} chars), maximum {1}";
"PATTERN": "String does not match pattern {0}: {1}";
}
// Schema and reference errors
interface SchemaErrors {
"UNRESOLVABLE_REFERENCE": "Reference could not be resolved: {0}";
"UNKNOWN_FORMAT": "There is no validation function for format '{0}'";
"ASYNC_TIMEOUT": "{0} asynchronous task(s) have timed out after {1} ms";
}Control how error paths are reported in validation results.
interface ErrorReportingOptions {
/** Report error paths as arrays instead of strings (default: false) */
reportPathAsArray?: boolean;
/** Stop after first error instead of collecting all errors (default: false) */
breakOnFirstError?: boolean;
}Usage Examples:
// String paths (default)
const validator1 = new ZSchema();
validator1.validate({ user: { name: 123 } }, schema);
const errors1 = validator1.getLastErrors();
console.log(errors1[0].path); // "#/user/name"
// Array paths
const validator2 = new ZSchema({ reportPathAsArray: true });
validator2.validate({ user: { name: 123 } }, schema);
const errors2 = validator2.getLastErrors();
console.log(errors2[0].path); // ["user", "name"]
// Fast-fail validation
const validator3 = new ZSchema({ breakOnFirstError: true });
validator3.validate(invalidData, schema);
const errors3 = validator3.getLastErrors();
console.log(errors3.length); // 1 (only first error)Validate only specific error types for performance optimization.
interface SelectiveValidationOptions {
/** Only check for specific error types during validation */
includeErrors?: string[];
}Usage Examples:
const validator = new ZSchema();
// Only check for type errors
const valid = validator.validate(data, schema, {
includeErrors: ["INVALID_TYPE"]
});
// Only check for format and length errors
const valid2 = validator.validate(data, schema, {
includeErrors: ["INVALID_FORMAT", "MIN_LENGTH", "MAX_LENGTH"]
});
// Performance benefit: skips other validation checks
const valid3 = validator.validate(data, schema, {
includeErrors: ["OBJECT_MISSING_REQUIRED_PROPERTY"]
});Install with Tessl CLI
npx tessl i tessl/npm-z-schema