or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

core-validation.mderror-handling.mdindex.mdjtd-schemas.mdkeywords-vocabularies.mdschema-2019.mdschema-2020.mdschema-management.mdstandalone-generation.mdtypescript-integration.md
tile.json

keywords-vocabularies.mddocs/

Keywords and Vocabularies

Extensible keyword system for creating custom validation logic, format validators, and vocabulary extensions that extend JSON Schema validation capabilities.

Capabilities

Add Keyword

Adds custom keyword definitions that extend the validation capabilities with custom logic, code generation, or macro transformations.

/**
 * Adds a custom keyword definition
 * @param definition - Keyword definition object
 * @returns The Ajv instance for chaining
 */
addKeyword(definition: KeywordDefinition): Ajv;

type KeywordDefinition = 
  | CodeKeywordDefinition 
  | FuncKeywordDefinition 
  | MacroKeywordDefinition;

interface BaseKeywordDefinition {
  keyword: string;
  type?: JSONType | JSONType[];
  schemaType?: JSONType | JSONType[];
  allowUndefined?: boolean;
  $data?: boolean;
  implements?: string[];
  before?: string;
  post?: boolean;
  metaSchema?: AnySchemaObject;
  validateSchema?: AnyValidateFunction;
  dependencies?: string[];
  error?: KeywordErrorDefinition;
}

interface CodeKeywordDefinition extends BaseKeywordDefinition {
  compile?: (schema: any, parentSchema: AnySchemaObject, it: SchemaCxt) => DataValidateFunction;
  validate?: DataValidateFunction;
  code?: (cxt: KeywordCxt, ruleType?: string) => void;
  macro?: (schema: any, parentSchema: AnySchemaObject, it: SchemaCxt) => AnySchema;
}

interface FuncKeywordDefinition extends BaseKeywordDefinition {
  validate: SchemaValidateFunction | DataValidateFunction;
  errors?: boolean;
  async?: boolean;
}

interface MacroKeywordDefinition extends BaseKeywordDefinition {
  macro: (schema: any, parentSchema: AnySchemaObject) => AnySchema;
}

Usage Examples:

import Ajv from "ajv";

const ajv = new Ajv();

// Simple validation function keyword
ajv.addKeyword({
  keyword: "range",
  type: "number",
  schemaType: "array",
  validate: function validateRange(schema: [number, number], data: number) {
    return data >= schema[0] && data <= schema[1];
  }
});

// Use the custom keyword
const schema = {
  type: "object",
  properties: {
    age: {
      type: "number",
      range: [0, 120]
    }
  }
};

const validate = ajv.compile(schema);
console.log(validate({ age: 25 })); // true
console.log(validate({ age: 150 })); // false

// Macro keyword that transforms to other schemas
ajv.addKeyword({
  keyword: "typeof",
  macro: function(schema: string) {
    return {
      type: "string",
      pattern: `^${schema}$`
    };
  }
});

// Code generation keyword for performance
ajv.addKeyword({
  keyword: "even",
  type: "number",
  code: function(cxt) {
    const { gen, data } = cxt;
    gen.if(`${data} % 2`, () => cxt.fail());
  }
});

Remove Keyword

Removes custom keyword definitions from the validator instance.

/**
 * Removes a keyword definition
 * @param keyword - Keyword name to remove
 * @returns The Ajv instance for chaining
 */
removeKeyword(keyword: string): Ajv;

Usage Examples:

import Ajv from "ajv";

const ajv = new Ajv();

// Add custom keyword
ajv.addKeyword({
  keyword: "custom",
  validate: () => true
});

// Remove the keyword
ajv.removeKeyword("custom");

// Keyword is no longer available
// This would now throw an error:
// ajv.compile({ custom: true });

Get Keyword

Retrieves information about a keyword definition, including built-in and custom keywords.

/**
 * Retrieves keyword definition information
 * @param keyword - Keyword name
 * @returns Keyword definition or false if not found
 */
getKeyword(keyword: string): AddedKeywordDefinition | false;

interface AddedKeywordDefinition extends KeywordDefinition {
  type: JSONType[];
  schemaType: JSONType[];
  $data: boolean;
  implements: string[];
}

Usage Examples:

import Ajv from "ajv";

const ajv = new Ajv();

// Check built-in keyword
const maxLengthDef = ajv.getKeyword("maxLength");
console.log(maxLengthDef); // Built-in keyword definition

// Check non-existent keyword
const nonExistent = ajv.getKeyword("nonexistent");
console.log(nonExistent); // false

// Add and check custom keyword
ajv.addKeyword({
  keyword: "positive",
  type: "number",
  validate: (schema: boolean, data: number) => schema ? data > 0 : true
});

const positiveDef = ajv.getKeyword("positive");
console.log(positiveDef); // Custom keyword definition

Add Vocabulary

Adds a collection of related keywords as a vocabulary, enabling organized extension of validation capabilities.

/**
 * Adds a vocabulary (collection of keywords)
 * @param vocabulary - Array of keyword definitions
 * @returns The Ajv instance for chaining
 */
addVocabulary(vocabulary: Vocabulary): Ajv;

type Vocabulary = KeywordDefinition[];

Usage Examples:

import Ajv from "ajv";

const ajv = new Ajv();

// Math vocabulary with related keywords
const mathVocabulary: Vocabulary = [
  {
    keyword: "divisibleBy",
    type: "number",
    validate: function(schema: number, data: number) {
      return data % schema === 0;
    }
  },
  {
    keyword: "prime", 
    type: "number",
    validate: function(schema: boolean, data: number) {
      if (!schema) return true;
      if (data < 2) return false;
      for (let i = 2; i * i <= data; i++) {
        if (data % i === 0) return false;
      }
      return true;
    }
  },
  {
    keyword: "fibonacci",
    type: "number", 
    validate: function(schema: boolean, data: number) {
      if (!schema) return true;
      let a = 0, b = 1;
      while (b < data) {
        [a, b] = [b, a + b];
      }
      return b === data || data === 0;
    }
  }
];

ajv.addVocabulary(mathVocabulary);

// Use vocabulary keywords
const schema = {
  type: "object",
  properties: {
    score: {
      type: "number",
      divisibleBy: 5,
      prime: false
    },
    sequence: {
      type: "number",
      fibonacci: true
    }
  }
};

Add Format

Adds custom format validators for string validation with support for both synchronous and asynchronous validation.

/**
 * Adds a custom format validator
 * @param name - Format name
 * @param format - Format definition (string, RegExp, or function)
 * @returns The Ajv instance for chaining
 */
addFormat(name: string, format: Format): Ajv;

type Format = string | RegExp | FormatDefinition<string> | FormatDefinition<number>;

interface FormatDefinition<T> {
  validate: FormatValidator<T>;
  compare?: FormatCompare<T>;
  async?: boolean;
}

type FormatValidator<T> = (data: T) => boolean;
type FormatCompare<T> = (data1: T, data2: T) => number | undefined;

Usage Examples:

import Ajv from "ajv";

const ajv = new Ajv();

// RegExp format
ajv.addFormat("username", /^[a-zA-Z0-9_]{3,16}$/);

// Function format with comparison
ajv.addFormat("credit-card", {
  validate: function(data: string) {
    // Luhn algorithm validation
    const digits = data.replace(/\D/g, '');
    if (digits.length < 13 || digits.length > 19) return false;
    
    let sum = 0;
    let isEven = false;
    
    for (let i = digits.length - 1; i >= 0; i--) {
      let digit = parseInt(digits[i]);
      if (isEven) {
        digit *= 2;
        if (digit > 9) digit -= 9;
      }
      sum += digit;
      isEven = !isEven;
    }
    
    return sum % 10 === 0;
  }
});

// Async format validation
ajv.addFormat("unique-email", {
  async: true,
  validate: async function(data: string) {
    // Simulate database check
    const response = await fetch(`/api/check-email/${data}`);
    const result = await response.json();
    return result.isUnique;
  }
});

// Use custom formats
const userSchema = {
  type: "object",
  properties: {
    username: {
      type: "string",
      format: "username"
    },
    email: {
      type: "string", 
      format: "unique-email"
    },
    creditCard: {
      type: "string",
      format: "credit-card"
    }
  }
};

Advanced Keyword Features

Error Customization

Custom keywords can provide detailed error information:

ajv.addKeyword({
  keyword: "isbn",
  error: {
    message: "must be a valid ISBN",
    params: (cxt) => ({ format: "isbn" })
  },
  validate: function validateISBN(schema: boolean, data: string) {
    if (!schema) return true;
    
    const isbn = data.replace(/[^0-9X]/gi, '').toUpperCase();
    if (isbn.length !== 10 && isbn.length !== 13) return false;
    
    // ISBN validation logic...
    return true; // simplified
  }
});

Schema Compilation Context

Keywords have access to compilation context for advanced features:

ajv.addKeyword({
  keyword: "excludeFields",
  code: function(cxt) {
    const { schema, gen, data } = cxt;
    const excluded = schema; // Array of field names to exclude
    
    gen.if(`${data} && typeof ${data} === "object"`, () => {
      excluded.forEach((field: string) => {
        gen.if(`${data}.hasOwnProperty(${JSON.stringify(field)})`, () => {
          cxt.fail({ message: `field ${field} is not allowed` });
        });
      });
    });
  }
});

Built-in Vocabularies

Ajv includes several built-in vocabularies:

  • Core Vocabulary: Basic validation keywords (type, enum, const)
  • Validation Vocabulary: Constraints (minimum, maximum, minLength, etc.)
  • Applicator Vocabulary: Schema composition (properties, items, allOf, etc.)
  • Format Vocabulary: String format validation
  • Meta-Data Vocabulary: Schema metadata (title, description, examples)

Each vocabulary can be selectively enabled or disabled based on your validation needs.