Асинхронная валидация в Yup опирается на возможность использовать
функции, возвращающие Promise, внутри пользовательских
правил проверки. Это позволяет интегрировать проверки, требующие
обращения к внешним источникам данных: API, базе данных, сервисам
авторизации и любым асинхронным вычислениям.
Основной механизм асинхронной валидации в Yup реализуется через метод
.test. Если функция теста возвращает Promise,
Yup автоматически переходит в режим ожидания результата.
import * as Yup from 'yup';
const schema = Yup.object({
email: Yup.string()
.email()
.required()
.test(
'check-email-unique',
'Такой email уже используется',
async (value) => {
if (!value) return true;
const response = await fetch(`/api/check-email?email=${value}`);
const data = await response.json();
return data.available === true;
}
),
});
Ключевая особенность заключается в том, что возвращаемое значение может быть:
true — проверка пройденаfalse — ошибка валидацииPromise<boolean> — асинхронная проверкаФункция .test получает доступ к контексту, который
позволяет работать с текущим значением объекта, путём к полю и
дополнительными параметрами валидации.
Yup.string().test('check', function (value) {
const { path, createError } = this;
return new Promise((resolve) => {
setTimeout(() => {
if (value !== 'admin') {
resolve(true);
} else {
resolve(
createError({
path,
message: 'Значение admin запрещено',
})
);
}
}, 500);
});
});
Использование this.createError позволяет формировать
кастомные ошибки с точной привязкой к полю схемы.
Асинхронные тесты в Yup выполняются последовательно в рамках одного поля, но схема в целом может валидировать несколько полей параллельно. Это создаёт важные поведенческие особенности:
await schema.validate(data, { abortEarly: false });
Опция abortEarly: false влияет только на синхронные
ошибки внутри одного поля, но не отменяет выполнение асинхронных
проверок.
.isValid и .validateМетод .validate возвращает либо валидированный объект,
либо выбрасывает ошибку, содержащую структуру нарушений.
try {
await schema.validate(data);
} catch (err) {
console.log(err.errors);
}
Метод .isValid используется для получения булевого
результата без выброса исключений:
const isValid = await schema.isValid(data);
В обоих случаях асинхронные тесты выполняются полностью, поскольку результат зависит от завершения всех Promise.
Типичный сценарий — проверка уникальности логина, email или другого идентификатора через сервер.
const schema = Yup.object({
username: Yup.string()
.required()
.test('unique-username', async function (value) {
const res = await fetch(`/api/users/check?username=${value}`);
const { exists } = await res.json();
if (exists) {
return this.createError({
message: 'Пользователь уже существует',
});
}
return true;
}),
});
Такая схема часто используется в формах регистрации, где локальная валидация дополняется серверной проверкой.
При частом изменении значения поля (например, ввод в input) асинхронные проверки могут создавать эффект гонки запросов. Старые запросы могут завершаться позже новых и перезаписывать результат.
Yup сам по себе не отменяет предыдущие Promise. Поэтому управление этой ситуацией обычно выносится на уровень приложения:
AbortControllerlet controller;
const schema = Yup.string().test('check', async (value) => {
if (controller) controller.abort();
controller = new AbortController();
const res = await fetch('/api/check', {
signal: controller.signal,
});
return res.ok;
});
.whenМетод .when позволяет включать асинхронные проверки
только при выполнении определённых условий.
const schema = Yup.object({
password: Yup.string().when('isAdmin', {
is: true,
then: (s) =>
s.test('check-admin-password', async (value) => {
const res = await fetch('/api/admin/check-password', {
method: 'POST',
body: JSON.stringify({ value }),
});
const data = await res.json();
return data.valid;
}),
}),
});
Такой подход снижает количество лишних сетевых запросов и делает схему более адаптивной.
Валидация массивов с асинхронными тестами выполняется для каждого элемента независимо.
const schema = Yup.object({
emails: Yup.array().of(
Yup.string().test('check-email', async (value) => {
const res = await fetch(`/api/check-email?email=${value}`);
const data = await res.json();
return data.available;
})
),
});
Каждый элемент массива получает собственный Promise, и итоговый результат формируется после завершения всех проверок.
Асинхронные валидаторы напрямую влияют на время выполнения схемы. При большом количестве полей с сетевыми запросами возникают характерные узкие места:
Оптимизация обычно включает:
Ошибка может быть возвращена как false, либо как объект
ошибки через createError.
return this.createError({
message: 'Ошибка проверки на сервере',
});
Также допустим вариант выбрасывания исключения внутри Promise, однако в Yup предпочтительно использовать явное возвращаемое значение.
Асинхронные проверки часто зависят от других значений схемы через
this.parent.
Yup.string().test('check-match', async function (value) {
const password = this.parent.password;
const res = await fetch('/api/check-password-match', {
method: 'POST',
body: JSON.stringify({ password, confirmation: value }),
});
const data = await res.json();
return data.match;
});
Это позволяет строить связные правила между полями без необходимости внешней логики.
Yup не предоставляет встроенного механизма отмены уже запущенных асинхронных тестов. Если схема запускается повторно, старые Promise продолжают выполняться в фоне. Это важно учитывать при интеграции с UI-библиотеками форм, где валидация может вызываться на каждый ввод символа.
Стратегии управления включают:
let validationId = 0;
Yup.string().test('check', async (value) => {
const currentId = ++validationId;
const res = await fetch('/api/check');
const result = await res.json();
return currentId === validationId && result.ok;
});
Такой подход предотвращает применение устаревших результатов.
addMethodРасширение Yup позволяет создавать переиспользуемые асинхронные правила.
Yup.addMethod(Yup.string, 'unique', function (message) {
return this.test('unique', message, async function (value) {
const res = await fetch(`/api/unique?value=${value}`);
const data = await res.json();
return data.unique;
});
});
const schema = Yup.object({
login: Yup.string().unique('Логин занят'),
});
Такой подход упрощает повторное использование асинхронных валидаторов в разных схемах.
Асинхронная валидация в Yup строится вокруг Promise-модели и интегрируется в синхронную структуру схемы без разделения API. Это создаёт единый механизм обработки правил, где: