Валидация строковых значений в схемах JSON часто требует не только базовых проверок типа или длины, но и проверки структуры: email, телефон, UUID, даты, идентификаторы внутренних систем. В библиотеке Ajv для этого используется механизм форматов (formats).
Формат в Ajv — это именованное правило проверки строкового значения,
которое применяется через ключ format в JSON Schema.
Пример использования встроенного формата:
{
"type": "string",
"format": "email"
}
В этом случае Ajv применяет стандартную проверку email. Однако встроенных форматов недостаточно для прикладных систем, поэтому предусмотрена возможность добавления собственных.
Добавление форматов осуществляется через метод
addFormat.
const Ajv = require("ajv");
const ajv = new Ajv();
ajv.addFormat("uppercase", {
type: "string",
validate: (data) => data === data.toUpperCase()
});
После регистрации формат может использоваться в схемах:
const schema = {
type: "string",
format: "uppercase"
};
При валидации значение будет проверяться на соответствие условию — все символы должны быть в верхнем регистре.
Наиболее простой вариант — использование регулярного выражения:
ajv.addFormat("productCode", /^[A-Z]{3}-\d{4}$/);
Такой формат проверяет строки вида:
ABC-1234
Регулярное выражение автоматически интерпретируется как функция проверки.
Особенности:
Функция предоставляет полный контроль над логикой проверки:
ajv.addFormat("even-number", {
type: "number",
validate: (value) => Number.isInteger(value) && value % 2 === 0
});
Формат может работать не только со строками, но и с числами, если
явно указан type.
Пример схемы:
{
type: "number",
format: "even-number"
}
Функциональные форматы используются при необходимости:
Функция может возвращать не только true/false, но и
строку с описанием ошибки:
ajv.addFormat("starts-with-A", {
type: "string",
validate: (value) => {
if (value.startsWith("A")) return true;
return "строка должна начинаться с буквы A";
}
});
В этом случае сообщение становится частью ошибки валидации.
Каждый формат может быть привязан к конкретному типу данных:
stringnumberbooleanintegerПример:
ajv.addFormat("positive-int", {
type: "integer",
validate: (value) => value > 0
});
Если тип значения не совпадает, формат не применяется.
При включённом строгом режиме Ajv может выдавать предупреждения или ошибки при использовании неизвестных форматов:
const ajv = new Ajv({
strict: true
});
Если схема содержит:
{ "format": "custom-format" }
и формат не зарегистрирован, возникает ошибка компиляции схемы.
Существующие форматы можно переопределять:
ajv.addFormat("email", {
type: "string",
validate: (value) => value.endsWith("@example.com")
});
В этом случае стандартная проверка email заменяется пользовательской логикой.
Такой подход используется для:
В некоторых конфигурациях поддерживается удаление форматов:
ajv.removeFormat("email");
После удаления использование формата в схемах приводит к ошибке или игнорированию, в зависимости от настроек строгого режима.
Форматы регистрируются на уровне экземпляра Ajv:
const ajv1 = new Ajv();
const ajv2 = new Ajv();
Формат, добавленный в ajv1, не доступен в
ajv2.
Для переиспользования применяется отдельный модуль или функция инициализации:
function registerFormats(ajv) {
ajv.addFormat("uuid", /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
}
ajv.addFormat("uuid", /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
Использование:
{
type: "string",
format: "uuid"
}
ajv.addFormat("phone", /^\+7\d{10}$/);
Формат фиксирует российский международный номер.
ajv.addFormat("iso-date", (value) => {
const d = new Date(value);
return !isNaN(d.getTime()) && value.includes("T");
});
ajv.addFormat("sku", {
type: "string",
validate: (v) => /^[A-Z]{2}-\d{6}$/.test(v)
});
Форматы выполняются на этапе валидации каждого значения, поэтому их эффективность критична:
validate увеличивают время
проверки;При массовой валидации (например, обработка массивов объектов) оптимизация форматов напрямую влияет на пропускную способность.
Форматы в Ajv имеют ряд ограничений:
Для более сложной логики используются custom keywords, а не formats.
Форматы работают совместно с другими ключами JSON Schema:
{
type: "string",
format: "uuid",
minLength: 36,
maxLength: 36
}
Проверка выполняется последовательно:
В крупных схемах форматы часто выносятся в единый слой и стандартизируются:
ajv.addFormat("order-id", /^[0-9]{10}$/);
ajv.addFormat("user-id", /^[A-Z0-9]{8}$/);
ajv.addFormat("timestamp", /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/);
Это позволяет:
Если значение не проходит проверку формата, Ajv формирует стандартную ошибку:
{
"keyword": "format",
"message": "must match format \"uuid\""
}
При использовании кастомных сообщений из validate они
добавляются вместо стандартного текста.
Форматы выполняют функцию декларативных ограничителей строки, отделяя:
Это разделение упрощает поддержку и масштабирование схем в сложных системах валидации.