Асинхронные форматы и ключевые слова

Валидация через format в Ajv применяется для проверки строковых значений на соответствие заранее определённым шаблонам: email, uri, дата, ip-адрес и другие. Механизм форматов изначально ориентирован на синхронную проверку, что определяет его ключевое ограничение при построении асинхронных схем.

Формат задаётся через ключ format в JSON Schema:

{
  "type": "string",
  "format": "email"
}

Внутри Ajv формат реализуется как функция, возвращающая булево значение:

ajv.addFormat('even-number', {
  type: 'number',
  validate: (data) => data % 2 === 0
});

Такая модель исключает возможность ожидания Promise или выполнения асинхронных операций. Любая попытка сделать формат асинхронным нарушает контракт синхронной валидации, на котором построен механизм format.


Ограничения форматов и причины синхронности

Система форматов выполняется в процессе обхода дерева схемы. Этот процесс предполагает:

  • линейную проверку значений;
  • отсутствие побочных асинхронных операций;
  • предсказуемое завершение без ожиданий.

Асинхронность в этом контексте приводит к разрушению модели выполнения:

  • невозможность агрегировать результаты в едином проходе;
  • усложнение обработки allErrors;
  • нарушение детерминированности результата.

По этой причине Ajv не допускает Promise в форматах, сохраняя их исключительно синхронными даже при использовании асинхронной валидации схем.


Асинхронные ключевые слова как замена форматам

Для сценариев, где требуется обращение к внешним источникам или выполнение операций с задержкой, используется механизм кастомных ключевых слов.

Ключевое отличие от форматов заключается в том, что keyword может быть объявлен как асинхронный:

ajv.addKeyword({
  keyword: 'isUniqueUser',
  async: true,
  type: 'string',
  validate: async (schema, data) => {
    const exists = await db.users.findByName(data);
    return !exists;
  }
});

Здесь validate возвращает Promise, а Ajv переключается в режим асинхронной валидации всей схемы.

Такая модель делает возможным:

  • запросы к базе данных;
  • обращение к HTTP API;
  • проверку внешних сервисов;
  • сложную бизнес-логику, зависящую от состояния системы.

Асинхронная компиляция схем

Для работы с асинхронными keyword используется специальный механизм компиляции:

const validate = ajv.compileAsync(schema);

validate(data)
  .then(valid => {
    // результат валидации
  })
  .catch(err => {
    // ошибка валидации
  });

Функция compileAsync отличается от обычной compile тем, что анализирует схему и определяет наличие асинхронных зависимостей.

Если схема содержит хотя бы один асинхронный keyword, весь валидатор становится Promise-based.


Модель выполнения асинхронной валидации

Асинхронная валидация в Ajv строится вокруг следующих принципов:

  1. Схема компилируется в функцию, возвращающую Promise.
  2. Каждый асинхронный keyword выполняется последовательно или параллельно в зависимости от структуры схемы.
  3. Результат агрегируется в единый итоговый ответ.

Пример структуры выполнения:

async function validate(data) {
  if (!await keyword1(data)) return false;
  if (!await keyword2(data)) return false;
  return true;
}

Такой подход сохраняет совместимость с синхронной логикой, но расширяет её возможностью ожидания внешних операций.


Взаимодействие форматов и асинхронных ключевых слов

Форматы и асинхронные keywords сосуществуют в одной системе, но имеют принципиально разные ограничения:

  • format всегда синхронен;
  • keyword может быть как синхронным, так и асинхронным;
  • асинхронная схема автоматически отключает синхронные предположения.

Если в схеме одновременно используется format и асинхронный keyword, порядок выполнения сохраняется:

  1. выполняются синхронные проверки (включая format);
  2. затем запускаются асинхронные проверки.

Практика проектирования асинхронных правил валидации

Асинхронные ключевые слова применяются в случаях, когда локальная проверка недостаточна:

  • проверка уникальности значений в базе данных;
  • валидация токенов через внешние сервисы;
  • контроль прав доступа;
  • проверка состояния ресурса в распределённой системе.

Пример комбинированной схемы:

const schema = {
  type: 'object',
  properties: {
    email: {
      type: 'string',
      format: 'email'
    },
    username: {
      type: 'string',
      isUniqueUser: true
    }
  }
};

В этом примере:

  • формат email выполняется синхронно;
  • isUniqueUser инициирует асинхронный запрос;
  • итоговая валидация становится Promise-based.

Управление ошибками в асинхронной модели

Асинхронная валидация формирует ошибки так же, как и синхронная, но с дополнительной стадией ожидания результата.

Структура ошибки включает:

  • путь к данным (instancePath);
  • сообщение (message);
  • тип нарушения (keyword);
  • параметры схемы (params).

Пример обработки:

try {
  await validate(data);
} catch (err) {
  console.log(err.errors);
}

Ошибки агрегируются после завершения всех асинхронных операций, что позволяет получить полный набор нарушений.


Производительность и поведение в асинхронном режиме

Асинхронная валидация в Ajv имеет более высокую стоимость по сравнению с синхронной:

  • увеличивается latency из-за ожидания I/O операций;
  • невозможна полная оптимизация потоковой проверки;
  • требуется управление конкурентностью запросов.

При проектировании схем важно учитывать:

  • минимизацию количества асинхронных keywords;
  • кэширование результатов внешних проверок;
  • разделение синхронной и асинхронной логики.

Сочетание синхронных форматов и асинхронных проверок

Комбинированный подход позволяет использовать сильные стороны обеих моделей:

  • format обеспечивает быстрые локальные проверки;
  • keyword async добавляет доступ к внешнему состоянию.

Такое разделение формирует двухуровневую систему валидации:

  1. структурная и синтаксическая проверка;
  2. семантическая проверка с обращением к внешним данным.