Валидация строковых данных в схемах Joi часто опирается на регулярные выражения, поскольку именно они позволяют описывать сложные шаблоны форматов: от простых ограничений на набор символов до строгих правил для паролей, телефонов и идентификаторов. Работа с регулярными выражениями в Joi встроена в методы описания строковых схем и тесно связана с механизмами кастомизации ошибок и комбинирования правил.
Основной инструмент для применения регулярных выражений в Joi — метод
pattern. Он позволяет задать правило соответствия строки
определённому регулярному выражению.
import Joi from 'joi';
const schema = Joi.string().pattern(/^[a-zA-Z0-9]{6,12}$/);
В данном случае строка должна состоять только из латинских букв и цифр, а её длина ограничена диапазоном от 6 до 12 символов.
Регулярное выражение работает по стандартным правилам JavaScript
RegExp, включая якоря ^ и $, которые фиксируют
соответствие всей строки целиком, а не её части.
Метод pattern допускает передачу объекта параметров, где
можно задать имя правила. Это полезно для более понятного описания
ошибок.
const schema = Joi.string().pattern(/^\d{4}-\d{2}-\d{2}$/, { name: 'date' });
Такое описание помогает различать типы ошибок при нескольких регулярных ограничениях в одной схеме.
Регулярные выражения часто применяются для проверки телефонных номеров, особенно в упрощённых форматах.
const phoneSchema = Joi.string().pattern(/^\+?[0-9]{10,15}$/);
Здесь допускается необязательный знак + и от 10 до 15
цифр подряд.
При более сложных требованиях, например разделении кода страны и номера, используется более детализированное выражение:
const phoneSchema = Joi.string().pattern(/^\+\d{1,3}-\d{4,14}$/);
Регулярные выражения позволяют задавать базовые требования к паролям: наличие цифр, букв разного регистра и специальных символов.
const passwordSchema = Joi.string().pattern(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{8,}$/
);
Разбор выражения:
(?=.*[a-z]) — минимум одна строчная буква(?=.*[A-Z]) — минимум одна заглавная буква(?=.*\d) — минимум одна цифра(?=.*[\W_]) — минимум один специальный символ{8,} — длина не менее 8 символовТакие конструкции часто применяются для первичной фильтрации паролей, но не заменяют полноценные политики безопасности.
Метод pattern может использоваться совместно с другими
строковыми правилами Joi:
const usernameSchema = Joi.string()
.min(3)
.max(20)
.pattern(/^[a-zA-Z0-9_]+$/);
Здесь комбинируются ограничения длины и допустимого набора символов.
Важно учитывать, что регулярное выражение в таких случаях не заменяет остальные проверки, а дополняет их. Joi применяет все правила последовательно.
Одна строка может проверяться сразу несколькими регулярными выражениями:
const schema = Joi.string()
.pattern(/[a-z]/)
.pattern(/[A-Z]/)
.pattern(/[0-9]/);
Каждое правило добавляет отдельное ограничение. В случае нарушения любого из них формируется отдельная ошибка.
Для управления сообщениями об ошибках используется метод
messages:
const schema = Joi.string()
.pattern(/[a-z]/)
.messages({
'string.pattern.base': 'Должна содержать строчные буквы'
});
Однако при нескольких шаблонах одинаковый ключ ошибки будет применяться ко всем, поэтому требуется более точная настройка.
Joi позволяет задавать сообщения для конкретных правил регулярных выражений через ключи контекста.
const schema = Joi.string()
.pattern(/^\d+$/, { name: 'digits' })
.messages({
'string.pattern.name': 'Значение должно содержать только цифры'
});
Также можно использовать базовый ключ:
.messages({
'string.pattern.base': 'Неверный формат строки'
});
Это сообщение применяется ко всем регулярным выражениям без имени.
В некоторых сценариях требуется запретить определённый шаблон. Для
этого используется параметр invert.
const schema = Joi.string().pattern(/admin/, { invert: true });
Такое правило запрещает появление подстроки admin в
любом месте строки.
Инверсия особенно полезна при фильтрации нежелательных слов, запрещённых паттернов или подозрительных последовательностей.
При работе с регулярными выражениями внутри Joi необходимо учитывать двойное экранирование при использовании строкового конструктора RegExp.
const schema = Joi.string().pattern(new RegExp('^\\d{3}-\\d{2}-\\d{4}$'));
В строковом представлении каждый обратный слеш требует удвоения, что часто становится источником ошибок при сложных шаблонах.
Использование литералов /.../ снижает вероятность ошибок
и повышает читаемость.
Joi полностью поддерживает JavaScript RegExp, включая флаги Unicode.
const schema = Joi.string().pattern(/^\p{L}+$/u);
Здесь допускаются только буквы любых языков. Флаг u
включает поддержку Unicode-свойств.
Это особенно важно при работе с международными данными, где
ограничение [a-zA-Z] оказывается недостаточным.
Хотя Joi предоставляет встроенный метод .email(), иногда
используются кастомные выражения:
const emailSchema = Joi.string().pattern(
/^[^\s@]+@[^\s@]+\.[^\s@]+$/
);
Такой вариант охватывает базовую структуру email, но не учитывает все спецификации RFC, поэтому используется только для упрощённой проверки.
Регулярные выражения могут участвовать в условных схемах через
when:
const schema = Joi.object({
type: Joi.string().valid('short', 'long'),
value: Joi.string().when('type', {
is: 'short',
then: Joi.string().pattern(/^\d{3}$/),
otherwise: Joi.string().pattern(/^\d{10}$/)
})
});
Здесь формат строки зависит от значения другого поля.
Сложные регулярные выражения могут влиять на производительность валидации, особенно при массовой обработке данных. Основные проблемы возникают при использовании:
(a+)+)(a|b|c|...)+)^ и $В контексте Joi это особенно критично, поскольку валидация часто выполняется на каждом входящем запросе.
Оптимальная практика — использовать простые и строго ограниченные выражения.
Одной из частых ошибок является отсутствие якорей:
Joi.string().pattern(/\d+/)
Такое выражение пропустит строки вида abc123xyz,
поскольку совпадение ищется внутри строки.
Корректный вариант:
Joi.string().pattern(/^\d+$/)
Другой распространённый случай — избыточно сложные выражения, которые
проще заменить комбинацией встроенных методов Joi
(alphanum, min, max).
Регулярные выражения в Joi не являются единственным инструментом ограничения строк. Они часто комбинируются с другими методами:
Joi.string()
.alphanum()
.min(5)
.max(15)
.pattern(/^[a-zA-Z]/);
Такой подход повышает читаемость и снижает нагрузку на регулярное выражение.
Joi полностью поддерживает lookahead и lookbehind (в зависимости от версии движка JavaScript):
Joi.string().pattern(/^(?=.*\d)(?=.*[a-zA-Z]).+$/);
Такие конструкции позволяют проверять наличие определённых классов символов без их извлечения.
В сложных схемах это помогает уменьшить количество отдельных правил.
При разработке схемы часто применяется поэтапное усложнение регулярного выражения:
Такой подход снижает риск ошибок и упрощает отладку.
Метод .describe() в Joi позволяет анализировать
структуру схемы, включая регулярные выражения:
const schema = Joi.string().pattern(/\d+/);
console.log(schema.describe());
В результате можно получить структурированное описание правил, что используется при генерации документации или динамическом анализе схем.