Библиотека Ajv поддерживает работу с JSON Schema как в синхронном, так и в асинхронном режиме. Асинхронность становится необходимой, когда правила валидации зависят от внешних источников данных: базы данных, HTTP-запросов, файловой системы или других сервисов.
Асинхронная модель в Ajv строится на Promise и полностью совместима с
async/await, что позволяет интегрировать валидацию в
современный поток выполнения JavaScript-кода без колбэков и
дополнительных обёрток.
При использовании асинхронных схем результат работы валидатора перестаёт быть булевым значением. Вместо этого функция валидации возвращает Promise, который:
resolve) при успешной валидации;reject) или возвращает false
через результат с ошибками при невалидных данных.Базовый пример асинхронной проверки:
import Ajv from "ajv";
const ajv = new Ajv();
const schema = {
type: "object",
properties: {
userId: { type: "string" }
},
required: ["userId"]
};
const validate = ajv.compile(schema);
async function run(data) {
const valid = await validate(data);
if (!valid) {
console.log(validate.errors);
}
}
Хотя пример выглядит синхронным, переход к async происходит автоматически, когда в схеме появляются асинхронные ключевые слова или пользовательские async-функции.
Основной механизм асинхронности в Ajv реализуется через пользовательские keywords.
Асинхронный keyword определяется как функция, возвращающая Promise:
ajv.addKeyword({
keyword: "isExistingUser",
async: true,
type: "string",
validate: async function (schema, data) {
const user = await database.findUserById(data);
return Boolean(user);
}
});
Использование в схеме:
const schema = {
type: "object",
properties: {
userId: { isExistingUser: true }
}
};
throw new Error()
внутри async-функции.Ajv предоставляет метод compileAsync, который
гарантирует корректную обработку схем, содержащих асинхронные
элементы:
const validate = await ajv.compileAsync(schema);
const valid = await validate(data);
Особенности:
В одной схеме могут одновременно присутствовать:
type,
minLength, pattern);custom keywords, внешние
API).Пример:
ajv.addKeyword({
keyword: "isUniqueEmail",
async: true,
validate: async function (schema, email) {
const exists = await api.checkEmail(email);
return !exists;
}
});
const schema = {
type: "object",
properties: {
email: {
type: "string",
format: "email",
isUniqueEmail: true
}
}
};
В таком случае Ajv строит гибридный pipeline, где синхронные проверки выполняются до первого асинхронного шага.
Использование async/await упрощает контроль потока
обработки данных, особенно в сервисах API:
async function createUser(data) {
const valid = await validate(data);
if (!valid) {
throw new Error("Validation failed");
}
return await database.saveUser(data);
}
Особенности паттерна:
try/catch.Частый сценарий — проверка уникальности, существования или доступности ресурсов.
ajv.addKeyword({
keyword: "existsInService",
async: true,
validate: async function (schema, value) {
const response = await fetch(`https://api.service.com/check/${value}`);
const result = await response.json();
return result.exists === true;
}
});
Такой подход делает схему зависимой от внешних систем, что требует учёта:
При частых проверках одинаковых значений целесообразно использовать кэширование:
const cache = new Map();
ajv.addKeyword({
keyword: "cachedCheck",
async: true,
validate: async function (schema, value) {
if (cache.has(value)) {
return cache.get(value);
}
const result = await expensiveCheck(value);
cache.set(value, result);
return result;
}
});
Кэш снижает нагрузку на внешние сервисы и уменьшает latency валидации.
Ошибки в async-логике могут возникать на нескольких уровнях:
validate: async () => {
throw new Error("Service unavailable");
}
Возникает при использовании fetch или других
HTTP-клиентов.
Асинхронная функция обязана возвращать boolean или Promise
Асинхронная модель влияет на производительность следующим образом:
Оптимизация достигается за счёт:
Возможна агрегация нескольких асинхронных проверок:
validate: async (schema, value) => {
const [a, b] = await Promise.all([
serviceA.check(value),
serviceB.check(value)
]);
return a && b;
}
Это снижает общее время валидации по сравнению с последовательными вызовами.
Асинхронная валидация естественно интегрируется в архитектуру сервисов:
async function handler(req, res) {
try {
const valid = await validate(req.body);
if (!valid) {
return res.status(400).json(validate.errors);
}
const result = await process(req.body);
res.json(result);
} catch (err) {
res.status(500).json({ message: err.message });
}
}
Такой подход объединяет:
Ajv позволяет строить модульные схемы, где async-валидация используется как отдельные блоки:
const userSchema = {
type: "object",
properties: {
email: { type: "string", isUniqueEmail: true },
id: { isExistingUser: true }
}
};
Композиция облегчает повторное использование логики и изоляцию внешних зависимостей.
Ajv выполняет асинхронную валидацию по внутреннему графу зависимостей схемы:
Это позволяет поддерживать предсказуемую модель выполнения даже при сложных схемах с множеством зависимостей.