В библиотеке Ajv схема JSON Schema не используется напрямую во время валидации. Перед проверкой данных она проходит этап компиляции, в результате которого создаётся специализированная функция-валидатор.
Компиляция схемы превращает декларативное описание правил в исполняемый JavaScript-код. Такой подход обеспечивает высокую производительность за счёт:
Скомпилированная схема в Ajv — это функция, содержащая встроенную логику валидации, а также дополнительные свойства (например, ссылки на исходную схему, параметры формата и метаданные).
Под сериализацией скомпилированной схемы понимается преобразование
результата ajv.compile(schema) в форму, которую можно:
Ключевая особенность: стандартная функция JavaScript не предназначена
для надёжной сериализации через JSON.stringify,
поскольку:
Поэтому в Ajv применяются специализированные механизмы генерации кода.
Попытка сериализовать результат компиляции напрямую приводит к ограниченному и нестабильному результату:
const validate = ajv.compile(schema);
JSON.stringify(validate); // {}
Причины:
Даже validate.toString() не решает задачу полностью:
const fnSource = validate.toString();
Такой подход:
Основной промышленный способ сериализации в Ajv — генерация автономного модуля валидации.
В Ajv предусмотрен механизм standalone-компиляции, при котором схема превращается в самодостаточный JavaScript-файл.
Пример:
import Ajv from "ajv";
import standaloneCode from "ajv/dist/standalone";
const ajv = new Ajv({ code: { source: true } });
const schema = {
type: "object",
properties: {
id: { type: "number" }
},
required: ["id"]
};
const validate = ajv.compile(schema);
const moduleCode = standaloneCode(ajv, validate);
На выходе получается строка JavaScript-кода, содержащая:
Сериализованный код может быть сохранён как обычный модуль:
import fs from "fs";
fs.writeFileSync("./validate.js", moduleCode);
После этого модуль используется без повторной компиляции:
import validate from "./validate.js";
validate({ id: 123 }); // true/false
Такой подход особенно полезен в сценариях:
Использование генерации кода даёт следующие эффекты:
1. Ускорение старта приложения
2. Уменьшение зависимости
3. Повышенная предсказуемость
Альтернативный подход — хранение уже скомпилированных функций в памяти:
const cache = new Map();
function getValidator(ajv, schema) {
const key = JSON.stringify(schema);
if (!cache.has(key)) {
cache.set(key, ajv.compile(schema));
}
return cache.get(key);
}
Этот метод:
Несмотря на наличие code generation, сериализация скомпилированных схем имеет ограничения:
1. Потеря динамических расширений
addKeyword кастомные проверки должны
быть заранее известны при генерации.2. Зависимость от конфигурации Ajv
strict, allErrors,
formats должны совпадать при компиляции и исполнении.3. Версионная совместимость
На практике сериализация часто переносится в этап build-time:
Пример pipeline:
dist/validatorsТиповой сценарий:
// build-time
const validateUser = ajv.compile(userSchema);
const code = standaloneCode(ajv, validateUser);
writeFile("validateUser.js", code);
// runtime
import validateUser from "./validateUser.js";
if (validateUser(data)) {
// данные валидны
}
Такой подход превращает Ajv из runtime-инструмента в compile-time систему валидации, где проверка данных становится обычной функцией без накладных расходов на интерпретацию схем.