validating-json-data
Validating JSON Data with z-schema
z-schema validates JSON data against JSON Schema (draft-04, draft-06, draft-07, draft-2019-09, draft-2020-12). Default draft: draft-2020-12.
Quick start
import ZSchema from 'z-schema';
const validator = ZSchema.create();
const schema = {
type: 'object',
properties: {
name: { type: 'string' },
age: { type: 'integer', minimum: 0 },
},
required: ['name'],
};
// Throws on invalid data
validator.validate({ name: 'Alice', age: 30 }, schema);
Install: npm install z-schema
Choosing a validation mode
z-schema has four modes based on two toggles: async and safe. Pick the one that fits the use case.
| Mode | Factory call | Returns | Use when |
|---|---|---|---|
| Sync throw | ZSchema.create() |
true or throws ValidateError |
Default — simple scripts and middleware |
| Sync safe | ZSchema.create({ safe: true }) |
{ valid, err? } |
Need to inspect errors without try/catch |
| Async throw | ZSchema.create({ async: true }) |
Promise<true> or rejects |
Using async format validators |
| Async safe | ZSchema.create({ async: true, safe: true }) |
Promise<{ valid, err? }> |
Async + non-throwing |
Sync throw (default)
import ZSchema from 'z-schema';
const validator = ZSchema.create();
try {
validator.validate(data, schema);
} catch (err) {
// err is ValidateError
console.log(err.details); // SchemaErrorDetail[]
}
Sync safe
const validator = ZSchema.create({ safe: true });
const result = validator.validate(data, schema);
if (!result.valid) {
console.log(result.err?.details);
}
Or call .validateSafe() on a regular (throwing) validator for the same result shape:
const validator = ZSchema.create();
const { valid, err } = validator.validateSafe(data, schema);
Async throw
Required when using async format validators.
const validator = ZSchema.create({ async: true });
try {
await validator.validate(data, schema);
} catch (err) {
console.log(err.details);
}
Async safe
const validator = ZSchema.create({ async: true, safe: true });
const { valid, err } = await validator.validate(data, schema);
Inspecting errors
ValidateError has .details — an array of SchemaErrorDetail:
interface SchemaErrorDetail {
message: string; // "Expected type string but found type number"
code: string; // "INVALID_TYPE"
params: (string | number | Array<string | number>)[];
path: string | Array<string | number>; // "#/age" or ["age"]
keyword?: string; // "type", "required", "minLength", etc.
inner?: SchemaErrorDetail[]; // sub-errors from anyOf/oneOf/not
schemaPath?: Array<string | number>;
schemaId?: string;
}
Example: walking nested errors
Combinators (anyOf, oneOf, not) produce nested inner errors:
const { valid, err } = validator.validateSafe(data, schema);
if (!valid && err) {
for (const detail of err.details) {
console.log(`${detail.path}: [${detail.code}] ${detail.message}`);
if (detail.inner) {
for (const sub of detail.inner) {
console.log(` └─ ${sub.path}: [${sub.code}] ${sub.message}`);
}
}
}
}
Filtering errors
Pass ValidateOptions as the third argument to include or exclude specific error codes:
// Only report type errors
validator.validate(data, schema, { includeErrors: ['INVALID_TYPE'] });
// Suppress string-length errors
validator.validate(data, schema, { excludeErrors: ['MIN_LENGTH', 'MAX_LENGTH'] });
For the full error code list, see references/error-codes.md.
Schema pre-compilation
Compile schemas at startup for better runtime performance and to resolve cross-references:
const validator = ZSchema.create();
const schemas = [
{
id: 'address',
type: 'object',
properties: { city: { type: 'string' }, zip: { type: 'string' } },
required: ['city'],
},
{
id: 'person',
type: 'object',
properties: {
name: { type: 'string' },
home: { $ref: 'address' },
},
required: ['name'],
},
];
// Compile all schemas (validates them and registers references)
validator.validateSchema(schemas);
// Validate data using a compiled schema ID
validator.validate({ name: 'Alice', home: { city: 'Paris' } }, 'person');
Remote references
Manual registration
ZSchema.setRemoteReference('http://example.com/schemas/address.json', addressSchema);
// or per-instance:
validator.setRemoteReference('http://example.com/schemas/person.json', personSchema);
Automatic loading via schema reader
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);
const remote = validator.getMissingRemoteReferences(err);
}
Custom format validators
Global (shared across all validator instances)
ZSchema.registerFormat('postal-code', (value) => {
return typeof value === 'string' && /^\d{5}(-\d{4})?$/.test(value);
});
Instance-scoped
const validator = ZSchema.create();
validator.registerFormat('postal-code', (value) => {
return typeof value === 'string' && /^\d{5}(-\d{4})?$/.test(value);
});
Via options at creation time
const validator = ZSchema.create({
customFormats: {
'postal-code': (value) => typeof value === 'string' && /^\d{5}(-\d{4})?$/.test(value),
},
});
Async format validators
Return Promise<boolean>. Requires { async: true }.
const validator = ZSchema.create({ async: true });
validator.registerFormat('user-exists', async (value) => {
if (typeof value !== 'number') return false;
const user = await db.findUser(value);
return user != null;
});
Listing registered formats
const formats = ZSchema.getRegisteredFormats();
Choosing a draft version
Set the draft explicitly if the schema targets a specific version:
const validator = ZSchema.create({ version: 'draft-07' });
Valid values: 'draft-04', 'draft-06', 'draft-07', 'draft2019-09', 'draft2020-12' (default), 'none'.
For a feature comparison across drafts, see references/draft-comparison.md.
Common options
| Option | Default | Purpose |
|---|---|---|
breakOnFirstError |
false |
Stop validation at the first error |
noEmptyStrings |
false |
Reject empty strings as type string |
noEmptyArrays |
false |
Reject empty arrays as type array |
strictMode |
false |
Enable all strict checks at once |
ignoreUnknownFormats |
false |
Suppress unknown format errors (modern drafts always ignore) |
formatAssertions |
null |
null=always assert, true=respect vocabulary, false=annotation-only |
reportPathAsArray |
false |
Report error paths as arrays instead of JSON Pointer strings |
For the full options reference, see references/options.md.
Validating sub-schemas
Target a specific path within a schema:
validator.validate(carData, fullSchema, { schemaPath: 'definitions.car' });
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>
TypeScript types
All types are exported from the package:
import type {
JsonSchema, // Schema type (all drafts union)
ZSchemaOptions, // Configuration options
ValidateOptions, // Per-call options (schemaPath, includeErrors, excludeErrors)
ValidateResponse, // { valid: boolean, err?: ValidateError }
SchemaErrorDetail, // Individual error detail
ErrorCode, // Error code string literal type
FormatValidatorFn, // (input: unknown) => boolean | Promise<boolean>
SchemaReader, // (uri: string) => JsonSchema
} from 'z-schema';
import { ValidateError } from 'z-schema';
Reference files
- references/error-codes.md — Full error code list with descriptions and examples
- references/options.md — Complete options reference with defaults
- references/draft-comparison.md — Feature comparison across JSON Schema drafts
Important conventions
- Always use
ZSchema.create(options?)— nevernew ZSchema(). The factory returns the correctly typed variant. - Error details are on
.details(not.errors). - Import types with
import type { ... }and values withimport { ValidateError }. - Default draft is
draft2020-12. Specify explicitly if targeting an older draft.
More from zaggino/z-schema
writing-json-schemas
Authors JSON Schema definitions for use with z-schema validation. Use when the user needs to write a JSON Schema, define a schema for an API payload, create schemas for form validation, structure schemas with $ref and $defs, choose between oneOf/anyOf/if-then-else, design object schemas with required and additionalProperties, validate arrays with items or prefixItems, add format constraints, organize schemas for reuse, or write draft-2020-12 schemas.
87skill-creator
Create, improve, and test skills for the z-schema JSON Schema validator library. Use this skill whenever the user wants to create a new skill from scratch, turn a workflow into a reusable skill, update or refine an existing skill, write test cases for a skill, or organize reference material for a skill. Also use when someone mentions "skill", "SKILL.md", or wants to document a z-schema workflow for reuse by humans or AI agents.
65migrating-json-schemas
Migrates JSON Schemas between draft versions for use with z-schema. Use when the user wants to upgrade schemas from draft-04 to draft-2020-12, convert between draft formats, update deprecated keywords, replace id with $id, convert definitions to $defs, migrate items to prefixItems, replace dependencies with dependentRequired or dependentSchemas, adopt unevaluatedProperties or unevaluatedItems, or adapt schemas to newer JSON Schema features.
65custom-format-validators
Registers and manages custom format validators in z-schema. Use when the user needs to add custom format validation, create sync or async format validators, register formats globally or per instance, validate emails or dates or phone numbers or custom business rules with format, configure formatAssertions for vocabulary-aware behavior, use customFormats option, list registered formats, handle async format timeouts, or understand how format validation differs across JSON Schema drafts.
64contributing-to-z-schema
Guides contributors through the z-schema codebase, PR workflow, and common development tasks. Use when the user wants to contribute to z-schema, add a new feature or keyword, add an error code, add a format validator, modify options, write tests, run the test suite, fix a failing test, understand the validation pipeline, navigate the source code architecture, or submit a pull request. Also use when someone mentions contributing, PRs, the z-schema source code, or the JSON Schema Test Suite integration.
64handling-validation-errors
Inspects, filters, and maps z-schema validation errors for application use. Use when the user needs to handle validation errors, walk nested inner errors from anyOf/oneOf/not combinators, map error codes to user-friendly messages, filter errors with includeErrors or excludeErrors, build form-field error mappers, use reportPathAsArray, interpret SchemaErrorDetail fields like code/path/keyword/inner, or debug why validation failed.
63