В практических задачах валидация редко ограничивается полным объектом, приходящим единовременно. Чаще структура данных изменяется по частям: обновляется одно поле, добавляется вложенный объект, применяется PATCH-запрос или форма отправляет неполный набор значений. В таких сценариях используется частичная валидация — проверка не всего объекта целиком, а его фрагмента с сохранением общей согласованности со схемой.
В Ajv это достигается сочетанием возможностей компиляции схем, повторного использования валидаторов и композиции JSON Schema.
По умолчанию Ajv проверяет объект относительно схемы целиком. Если в схеме объявлено обязательное поле, а его нет в переданных данных, результат будет ошибочным:
const Ajv = require("ajv");
const ajv = new Ajv();
const schema = {
type: "object",
properties: {
name: { type: "string" },
age: { type: "number" }
},
required: ["name", "age"],
additionalProperties: false
};
const validate = ajv.compile(schema);
validate({ name: "Ivan" }); // false
console.log(validate.errors);
Такая модель соответствует полной валидации, но не подходит для частичных обновлений.
Первый уровень частичной валидации — изменение схемы так, чтобы она допускала неполные данные.
const schema = {
type: "object",
properties: {
name: { type: "string" },
age: { type: "number" }
},
additionalProperties: false
};
Теперь объект { name: "Ivan" } считается валидным, так
как отсутствует блок required.
Такой подход превращает схему в «описание формы данных», а не строгий контракт.
Более управляемый способ — разделение схемы на фрагменты и выборочная валидация.
const nameSchema = {
type: "object",
properties: {
name: { type: "string", minLength: 2 }
},
required: ["name"],
additionalProperties: false
};
const ageSchema = {
type: "object",
properties: {
age: { type: "number", minimum: 0 }
},
required: ["age"],
additionalProperties: false
};
Каждый фрагмент компилируется отдельно:
const validateName = ajv.compile(nameSchema);
const validateAge = ajv.compile(ageSchema);
validateName({ name: "Ivan" }); // true
validateAge({ age: 30 }); // true
Этот подход используется при:
При обновлении одного поля нет необходимости валидировать весь объект. Вместо этого применяется локальная проверка изменения.
const user = {
name: "Ivan",
age: 30
};
// обновление только age
const patch = { age: 31 };
validateAge(patch); // проверяется только изменённая часть
Object.assign(user, patch);
Такой подход снижает вычислительную нагрузку и упрощает контроль ошибок.
Частичная валидация часто требует контекстной логики: допустимость поля зависит от другого поля.
const schema = {
type: "object",
properties: {
role: { type: "string" },
accessLevel: { type: "number" }
},
if: {
properties: { role: { const: "admin" } }
},
then: {
required: ["accessLevel"]
}
};
Если передан только role, Ajv применяет условие без
необходимости наличия остальных полей.
Это позволяет валидировать фрагменты данных, сохраняя бизнес-логику.
При работе с неполными структурами важно контролировать лишние поля, особенно если данные поступают по частям.
const ajv = new Ajv({ removeAdditional: "all" });
Опция позволяет автоматически очищать объект от незадекларированных свойств.
При частичной валидации это полезно, когда каждое обновление должно строго соответствовать своему фрагменту схемы.
Ajv поддерживает разбиение схем на модули с помощью
$ref, что позволяет валидировать отдельные части структуры
независимо.
const addressSchema = {
$id: "address",
type: "object",
properties: {
city: { type: "string" },
street: { type: "string" }
},
required: ["city"]
};
const userSchema = {
type: "object",
properties: {
name: { type: "string" },
address: { $ref: "address" }
}
};
Компиляция:
ajv.addSchema(addressSchema);
const validateUser = ajv.compile(userSchema);
Теперь можно отдельно валидировать только address:
const validateAddress = ajv.getSchema("address");
validateAddress({ city: "Almaty" }); // true
Такой подход формирует естественную модель частичной проверки вложенных структур.
JSON Patch и Merge Patch часто требуют проверки только изменяемого фрагмента. В Ajv это реализуется через изолированные схемы операций.
Пример для PATCH:
const patchSchema = {
type: "object",
properties: {
op: { enum: ["replace", "add", "remove"] },
path: { type: "string" },
value: {}
},
required: ["op", "path"]
};
Каждый элемент массива patch проверяется отдельно:
const validatePatch = ajv.compile(patchSchema);
validatePatch({ op: "replace", path: "/name", value: "Alex" });
Это пример атомарной частичной валидации, где каждая операция независима.
В сложных структурах часто требуется проверять только конкретный уровень вложенности.
const schema = {
type: "object",
properties: {
user: {
type: "object",
properties: {
profile: {
type: "object",
properties: {
name: { type: "string" }
}
}
}
}
}
};
Вместо полной валидации всего объекта можно выделить фрагмент:
const profileSchema = schema.properties.user.properties.profile;
const validateProfile = ajv.compile(profileSchema);
validateProfile({ name: "Ivan" });
Такой подход используется при редактировании вложенных сущностей без затрагивания верхнего уровня.
В строгом режиме Ajv усиливает контроль схемы и выявляет несоответствия структуры ещё на этапе компиляции.
const ajv = new Ajv({ strict: true });
При частичной валидации это помогает обнаруживать:
$refСтрогий режим особенно полезен при разбиении схем на независимые части.
Частичная валидация часто приводит к множеству мелких схем. Повторная компиляция становится узким местом, поэтому используется кеширование:
const validators = new Map();
function getValidator(schemaKey, schema) {
if (!validators.has(schemaKey)) {
validators.set(schemaKey, ajv.compile(schema));
}
return validators.get(schemaKey);
}
Это обеспечивает стабильную производительность при частых частичных проверках.
При проверке фрагментов данных важно учитывать особенности формирования ошибок:
const validate = ajv.compile({
type: "object",
required: ["name"],
properties: {
name: { type: "string" }
}
});
validate({}); // false, но причина локальна — отсутствует name
В частичной модели такие ошибки интерпретируются иначе: отсутствие поля может быть допустимым состоянием.
В распределённых системах частичная валидация применяется на уровне сервисов:
Каждый сервис использует собственный набор схем Ajv без зависимости от глобального объекта.
На практике оба подхода используются одновременно:
if (validatePatch(patch)) {
Object.assign(entity, patch);
validateFull(entity);
}
Такая модель обеспечивает баланс между гибкостью и целостностью данных.