Latest JSON Schema Draft 2020-12 validation with all modern features, improvements, and the most current specification compliance for cutting-edge schema validation.
Creates an Ajv validator instance configured for JSON Schema Draft 2020-12 with all latest features automatically enabled.
/**
* Creates Ajv validator for JSON Schema Draft 2020-12
* @param options - Configuration options (dynamicRef, next, unevaluated enabled by default)
*/
constructor(options?: Options);
class Ajv2020 extends AjvCore {
constructor(opts: Options = {}) {
super({
...opts,
dynamicRef: true, // Automatically enabled
next: true, // Automatically enabled
unevaluated: true, // Automatically enabled
});
}
}Usage Examples:
import Ajv2020 from "ajv/dist/2020";
// Basic 2020-12 validator with latest features
const ajv = new Ajv2020();
// Custom options while maintaining 2020-12 features
const ajvCustom = new Ajv2020({
allErrors: true,
verbose: true,
strict: false,
discriminator: true
});Enhanced vocabulary system allowing fine-grained control over which JSON Schema features are enabled.
// Vocabulary control in schemas
interface VocabularySchema {
$vocabulary?: {
[vocabularyId: string]: boolean;
};
[key: string]: any;
}Usage Examples:
import Ajv2020 from "ajv/dist/2020";
const ajv = new Ajv2020();
// Schema with explicit vocabulary requirements
const schema = {
$schema: "https://json-schema.org/draft/2020-12/schema",
$vocabulary: {
"https://json-schema.org/draft/2020-12/vocab/core": true,
"https://json-schema.org/draft/2020-12/vocab/applicator": true,
"https://json-schema.org/draft/2020-12/vocab/validation": true,
"https://json-schema.org/draft/2020-12/vocab/format-annotation": false
},
type: "object",
properties: {
name: { type: "string" },
email: {
type: "string",
// format keyword ignored due to vocabulary setting
format: "email"
}
}
};Modern array validation with prefixItems for tuple validation and enhanced items behavior.
// Modern array validation syntax
interface ArraySchema2020 {
prefixItems?: AnySchema[]; // Schemas for specific positions
items?: AnySchema; // Schema for additional items
unevaluatedItems?: AnySchema | boolean;
minItems?: number;
maxItems?: number;
uniqueItems?: boolean;
contains?: AnySchema;
minContains?: number;
maxContains?: number;
}Usage Examples:
import Ajv2020 from "ajv/dist/2020";
const ajv = new Ajv2020();
// Tuple validation with prefixItems
const coordinateSchema = {
type: "array",
prefixItems: [
{ type: "number", description: "x coordinate" },
{ type: "number", description: "y coordinate" },
{ type: "number", description: "z coordinate", default: 0 }
],
minItems: 2,
maxItems: 3,
unevaluatedItems: false // No additional items allowed
};
const validateCoordinate = ajv.compile(coordinateSchema);
console.log(validateCoordinate([10, 20])); // true - 2D coordinate
console.log(validateCoordinate([10, 20, 30])); // true - 3D coordinate
console.log(validateCoordinate([10, 20, 30, 40])); // false - too many items
// Mixed array validation
const mixedArraySchema = {
type: "array",
prefixItems: [
{ type: "string", description: "header" },
{ type: "number", description: "version" }
],
items: {
type: "object",
properties: {
id: { type: "string" },
value: { type: "number" }
},
required: ["id", "value"]
},
minItems: 2
};
// Valid: header, version, then objects
const validMixed = ["Header", 1.0, { id: "a", value: 10 }, { id: "b", value: 20 }];
console.log(ajv.validate(mixedArraySchema, validMixed)); // trueEnhanced dynamic anchor system with clearer semantics and better resolution.
// Dynamic anchor patterns in 2020-12
interface DynamicAnchor2020 {
$dynamicAnchor?: string; // Improved dynamic anchor definition
$dynamicRef?: string; // Enhanced dynamic reference resolution
[key: string]: any;
}Usage Examples:
import Ajv2020 from "ajv/dist/2020";
const ajv = new Ajv2020();
// Meta-schema pattern with dynamic anchors
const baseMetaSchema = {
$id: "https://example.com/meta/base",
$dynamicAnchor: "meta",
type: "object",
properties: {
type: { type: "string" },
title: { type: "string" },
description: { type: "string" }
}
};
// Extended meta-schema
const extendedMetaSchema = {
$id: "https://example.com/meta/extended",
$dynamicAnchor: "meta",
allOf: [{ $ref: "https://example.com/meta/base" }],
properties: {
type: { type: "string" },
title: { type: "string" },
description: { type: "string" },
examples: {
type: "array",
items: true
},
deprecated: { type: "boolean" }
}
};
// Schema that uses dynamic resolution
const schemaUsingMeta = {
$id: "https://example.com/schema-with-meta",
type: "object",
properties: {
schema: { $dynamicRef: "#meta" }
}
};
ajv.addSchema([baseMetaSchema, extendedMetaSchema, schemaUsingMeta]);
// Resolution depends on which meta-schema is in scope
const validate = ajv.compile({
allOf: [
{ $ref: "https://example.com/meta/extended" },
{ $ref: "https://example.com/schema-with-meta" }
]
});Enhanced content validation for strings with specific media types and encodings.
// Content validation keywords
interface ContentSchema {
contentMediaType?: string; // MIME type of content
contentEncoding?: string; // Encoding of content (base64, etc.)
contentSchema?: AnySchema; // Schema for decoded content
[key: string]: any;
}Usage Examples:
import Ajv2020 from "ajv/dist/2020";
const ajv = new Ajv2020();
// JSON content validation
const configSchema = {
type: "object",
properties: {
config: {
type: "string",
contentMediaType: "application/json",
contentSchema: {
type: "object",
properties: {
apiKey: { type: "string" },
timeout: { type: "number", minimum: 0 }
},
required: ["apiKey"]
}
},
certificate: {
type: "string",
contentEncoding: "base64",
contentMediaType: "application/x-pem-file"
}
}
};
const validate = ajv.compile(configSchema);
// Valid JSON content
console.log(validate({
config: '{"apiKey": "abc123", "timeout": 5000}',
certificate: "LS0tLS1CRUdJTi0..." // base64 encoded certificate
})); // true
// Invalid JSON content
console.log(validate({
config: '{"timeout": 5000}', // missing required apiKey
certificate: "LS0tLS1CRUdJTi0..."
})); // falseComprehensive annotation collection system for gathering metadata during validation.
// Annotation collection in validation results
interface AnnotationCollection {
annotations?: {
[instancePath: string]: {
[keyword: string]: any;
};
};
}Usage Examples:
import Ajv2020 from "ajv/dist/2020";
const ajv = new Ajv2020({
verbose: true, // Enable annotation collection
allErrors: true
});
// Schema with annotations
const productSchema = {
type: "object",
title: "Product",
description: "A product in our catalog",
properties: {
name: {
type: "string",
title: "Product Name",
examples: ["Widget", "Gadget"]
},
price: {
type: "number",
title: "Price in USD",
minimum: 0,
examples: [9.99, 29.99, 199.99]
},
category: {
type: "string",
title: "Product Category",
enum: ["electronics", "clothing", "books"],
default: "electronics"
}
},
required: ["name", "price"],
examples: [
{ name: "Smartphone", price: 599.99, category: "electronics" },
{ name: "T-Shirt", price: 19.99, category: "clothing" }
]
};
const validate = ajv.compile(productSchema);
const product = { name: "Laptop", price: 999.99 };
const valid = validate(product);
// Access collected annotations
if (validate.evaluated) {
console.log("Annotations:", validate.evaluated.annotations);
console.log("Dynamic defaults:", validate.evaluated.dynamicDefaults);
}Improved error reporting with better path resolution and more detailed error context.
// Enhanced error objects in 2020-12
interface ErrorObject2020 extends ErrorObject {
instancePath: string; // JSONPointer to the data
schemaPath: string; // JSONPointer to the schema
unevaluated?: boolean; // Whether error relates to unevaluated data
dynamicAnchor?: string; // Dynamic anchor context if applicable
}Usage Examples:
import Ajv2020 from "ajv/dist/2020";
const ajv = new Ajv2020({ allErrors: true, verbose: true });
const complexSchema = {
type: "object",
properties: {
user: {
type: "object",
properties: {
profile: {
type: "object",
properties: {
name: { type: "string", minLength: 1 },
email: { type: "string", format: "email" }
},
required: ["name", "email"],
unevaluatedProperties: false
}
}
}
}
};
const invalidData = {
user: {
profile: {
name: "", // too short
email: "invalid-email", // invalid format
extra: "not allowed" // unevaluated property
}
}
};
const valid = ajv.validate(complexSchema, invalidData);
if (!valid && ajv.errors) {
ajv.errors.forEach(error => {
console.log(`Error at ${error.instancePath}: ${error.message}`);
console.log(`Schema location: ${error.schemaPath}`);
console.log(`Keyword: ${error.keyword}`);
if (error.params) {
console.log(`Parameters:`, error.params);
}
});
}Key improvements in 2020-12 over 2019-09:
prefixItems replaces array-form itemscontentSchemaMigration Example:
// 2019-09 array validation
const schema2019 = {
type: "array",
items: [
{ type: "string" }, // First item
{ type: "number" } // Second item
],
additionalItems: false // No more items
};
// 2020-12 equivalent
const schema2020 = {
type: "array",
prefixItems: [
{ type: "string" }, // First item
{ type: "number" } // Second item
],
unevaluatedItems: false // No more items
};