Валидация JSON Schema: проверяем JSON в Node, Python и браузере (2026)
Кратко — JSON Schema это контракт для данных JSON: вы объявляете типы полей, обязательные ключи и ограничения, а валидатор проверяет, соответствует ли документ JSON этому контракту. В Node берите Ajv для самой быстрой проверки, в Python — библиотеку jsonschema для переносимых схем, в браузере подключайте Ajv ради мгновенной обратной связи в формах и конфигурациях. Для новых проектов в 2026 году берите Draft 2020-12.
Ниже минимальный рабочий пример, сквозные шаблоны для всех трёх сред исполнения и список ловушек, из-за которых возникают баги в духе «валидация прошла, а production отверг данные».
Что такое JSON Schema (и чем оно не является)
Определение в одном предложении
JSON Schema это документ JSON, описывающий форму других документов JSON. Валидатор читает схему и данные, после чего подтверждает соответствие или возвращает пути, где проверка не прошла.
Минимальный полезный пример:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": { "name": { "type": "string" } },
"required": ["name"]
}
{"name": "Alice"} проходит. {"age": 30} не проходит (отсутствует name). {"name": 42} не проходит (name это не строка). Это вся ментальная модель.
JSON Schema против проверки синтаксиса JSON
Две разные задачи, которые часто путают.
| Параметр | Проверка синтаксиса JSON | Валидация JSON Schema |
|---|---|---|
| Что проверяет | Является ли это допустимым документом JSON? | Соответствует ли JSON контракту? |
| Что ловит | Пропущенные запятые, одинарные кавычки, комментарии | Неверные типы, отсутствие обязательных полей, выход за диапазон |
| Инструменты | JSON.parse(), Форматировщик JSON | Ajv, jsonschema (Python), fastjsonschema |
| Когда применять | Самым первым шагом, до разбора | Сразу после разбора, до бизнес-логики |
На практике делают и то, и другое: красиво распечатывают payload в Форматировщике JSON, чтобы убедиться, что его удаётся разобрать, а затем прогоняют через схему, чтобы подтвердить соответствие контракту.
JSON Schema против JSONPath, JSON Patch, jq и TypeScript
Эту проблемную область делят пять инструментов. Матрица решений:
| Инструмент | На какой вопрос отвечает | Когда тянуться за ним |
|---|---|---|
| JSON Schema | Соответствует ли этот JSON ожидаемой структуре? | Валидация ввода API, конфигурационных файлов, payload форм |
| JSONPath | Как извлечь значение из этого JSON? | Извлечение вложенных полей, пакетное чтение |
| JSON Patch (RFC 6902) | Как описать различие между A и B? | Совместное редактирование, инкрементальная синхронизация |
| jq | Как обработать JSON в командной строке? | Shell-скрипты, конвейеры логов, CI-проверки |
| Типы TypeScript | Корректно ли мой код использует эту форму? | Гарантии времени компиляции внутри одного codebase |
Главное разделение простое: JSON Schema проверяет неизвестные данные во время выполнения, а TypeScript проверяет известный код во время компиляции. TypeScript не поможет с JSON из стороннего webhook или вставленного пользователем; для этого нужен JSON Schema. Zod и Pydantic занимают промежуточную нишу (типы времени компиляции плюс валидация во время выполнения), о них речь пойдёт ниже.
JSON Schema против OpenAPI
Распространённое заблуждение: будто OpenAPI заменяет JSON Schema. Это не так. OpenAPI использует JSON Schema внутри себя для описания тел запросов и ответов, а сверху наслаивает пути, параметры, схемы безопасности и адреса серверов. Схема — это контракт формы данных; OpenAPI — контракт API, который её оборачивает.
| Измерение | JSON Schema | OpenAPI |
|---|---|---|
| Область | Форма одного документа JSON | Форма целого HTTP API |
| Зависимости | Нет (схема — самодостаточный JSON) | Импортирует JSON Schema для описаний тел |
| Привязка версий | Draft 7 / Draft 2019-09 / Draft 2020-12 | OpenAPI 3.0 использует подмножество Draft 4; OpenAPI 3.1 нативно использует Draft 2020-12 |
| Типичное применение | Конфигурационные файлы, конверты сообщений, валидация форм, контракты на одиночный payload | Проектирование REST API, генерация SDK, mock-серверы, контрактное тестирование |
| Кодогенерация | Ограничена (некоторые инструменты в духе quicktype) | Зрелая экосистема (openapi-generator, oapi-codegen, вендорские SDK) |
| Управление контрактами | По одному файлу на форму, без маршрутизации | Пути, операции, потоки авторизации, версионируемые эндпоинты в одном документе |
Тянитесь за чистой JSON Schema, когда артефакт, который вас интересует, — это один документ: payload webhook, конфигурационный файл, сообщение очереди или форма. HTTP-поверхности для описания нет, поэтому OpenAPI становится излишним.
Тянитесь за OpenAPI, когда вы публикуете HTTP API и хотите одним документом управлять документацией, генерацией SDK, mock-серверами и контрактными тестами. Сначала опишите схемы как самостоятельные файлы JSON Schema в каталоге schemas/, а затем подключайте их через $ref из документа OpenAPI. Так схемы остаются переиспользуемыми и вне контекста API.
Привязка версий ставит команды в тупик. OpenAPI 3.0 использует подмножество Draft 4, поэтому внутри документа 3.0 нельзя применять ключевые слова Draft 2020-12, такие как prefixItems или unevaluatedProperties — генераторы их молча проигнорируют. OpenAPI 3.1 — надмножество Draft 2020-12, поэтому всё допустимое в 2020-12 допустимо и в 3.1. Если есть выбор, целитесь в OpenAPI 3.1 и пишите схемы Draft 2020-12 повсюду.
Ваша первая JSON Schema (5 минут)
Ключевые слова, которые понадобятся в первую очередь
С этим набором вы пройдёте 80% пути:
{
"type": "object",
"properties": {
"id": { "type": "integer", "minimum": 1 },
"email": { "type": "string", "format": "email" },
"age": { "type": "integer", "minimum": 0, "maximum": 150 },
"tags": { "type": "array", "items": { "type": "string" }, "minItems": 1 },
"role": { "enum": ["admin", "editor", "viewer"] },
"metadata": { "type": "object", "additionalProperties": true }
},
"required": ["id", "email"],
"additionalProperties": false
}
Словарь:
type—string,number,integer,boolean,null,array,objectproperties+required— объявляют поля и помечают обязательныеenum/const— ограничивают фиксированным набором или единственным литераломminimum/maximum/multipleOf— числовые границыminLength/maxLength/pattern— длина строки и regexminItems/maxItems/uniqueItems— форма массиваadditionalProperties: false— отвергать необъявленные ключи (всегда выставляйте это во входных контрактах)
Примеры JSON Schema по сценариям
Перечисленные выше ключевые слова всплывают в разных комбинациях в зависимости от того, что вы валидируете. Несколько показательных форм:
Тело запроса API — эндпоинт регистрации, принимающий email и пароль:
{
"type": "object",
"properties": {
"email": { "type": "string", "format": "email" },
"password": { "type": "string", "minLength": 8, "maxLength": 128 }
},
"required": ["email", "password"],
"additionalProperties": false
}
Конфигурационный файл — настройки логгера, фиксирующие уровень в виде ограниченного набора:
{
"type": "object",
"properties": {
"level": { "enum": ["debug", "info", "warn", "error"] },
"output": { "type": "string", "default": "stdout" }
},
"required": ["level"],
"additionalProperties": false
}
Payload формы с условными правилами — когда accountType равен "business", поле taxId становится обязательным:
{
"type": "object",
"properties": {
"accountType": { "enum": ["personal", "business"] },
"taxId": { "type": "string" }
},
"if": { "properties": { "accountType": { "const": "business" } } },
"then": { "required": ["taxId"] }
}
Запись JSON из строки CSV — одна строка экспортированной таблицы заказов:
{
"type": "object",
"properties": {
"orderId": { "type": "string", "pattern": "^ORD-[0-9]{6}$" },
"orderedOn": { "type": "string", "format": "date" },
"totalUsd": { "type": "number", "minimum": 0 }
},
"required": ["orderId", "orderedOn", "totalUsd"]
}
Конверт события webhook — oneOf различает варианты по литералу type, поэтому у каждой разновидности события собственная форма payload:
{
"oneOf": [
{ "properties": { "type": { "const": "order.created" }, "data": { "$ref": "#/$defs/order" } } },
{ "properties": { "type": { "const": "order.refunded" }, "data": { "$ref": "#/$defs/refund" } } }
]
}
Эти пять примеров покрывают основную массу того, что команды пишут на практике. Скопируйте ближайший образец и подправьте имена полей — словарь ключевых слов остаётся прежним.
Валидация без установки чего-либо
Вставьте схему и payload в playground на ajv.js.org или jsonschemavalidator.net и получите мгновенный вердикт. Если сам JSON выглядит подозрительно, сначала прогоните его через Форматировщик JSON.
Валидация в Node.js с помощью Ajv
Установка и пример из 12 строк
Ajv компилирует вашу схему в оптимизированную функцию при первом вызове compile, а затем переиспользует её.
npm install ajv
import Ajv from "ajv";
const ajv = new Ajv();
const schema = {
type: "object",
properties: {
name: { type: "string" },
age: { type: "integer", minimum: 0 }
},
required: ["name"]
};
const validate = ajv.compile(schema);
const data = { name: "Alice", age: 30 };
if (!validate(data)) console.log(validate.errors);
else console.log("OK");
Переключение на Draft 2020-12
Конструктор Ajv по умолчанию по соображениям обратной совместимости всё ещё привязан к Draft 7. Включите 2020-12 явно:
import Ajv2020 from "ajv/dist/2020";
const ajv = new Ajv2020({ strict: true, allErrors: true });
Теперь доступны prefixItems, unevaluatedProperties и $dynamicRef. Что делает каждое из них, смотрите в разделе про Draft 2020-12 ниже.
Включаем валидацию format
На этой особенности Ajv спотыкается больше разработчиков, чем на любой другой: format: "email" по умолчанию ничего не делает. Спецификация рассматривает format как рекомендательный, поэтому модуль форматов нужно зарегистрировать самостоятельно:
npm install ajv-formats
import addFormats from "ajv-formats";
addFormats(ajv); // теперь "format": "email" действительно валидируется
Без этого шага {"email": "not-an-email"} пройдёт схему, требующую format: "email". В production всегда устанавливайте ajv-formats.
Express middleware на боевой нагрузке
Один валидатор на маршрут, скомпилированный при старте:
import express from "express";
import Ajv2020 from "ajv/dist/2020";
import addFormats from "ajv-formats";
const ajv = new Ajv2020({ allErrors: true });
addFormats(ajv);
const validateUser = ajv.compile({
type: "object",
properties: {
email: { type: "string", format: "email" },
age: { type: "integer", minimum: 13 }
},
required: ["email"],
additionalProperties: false
});
const app = express();
app.use(express.json());
app.post("/users", (req, res) => {
if (!validateUser(req.body)) {
return res.status(400).json({ errors: validateUser.errors });
}
// ... бизнес-логика
res.status(201).json({ ok: true });
});
Самая дорогая типичная ошибка — вызывать ajv.compile(schema) внутри обработчика запроса. Скомпилируйте схему один раз на уровне модуля и переиспользуйте возвращённую функцию. Перекомпиляция на каждый запрос проседает пропускную способность в 50 раз и сильнее.
Валидация в Python с помощью jsonschema
Установка и базовое использование
pip install jsonschema
from jsonschema import validate, ValidationError
schema = {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer", "minimum": 0}
},
"required": ["name"]
}
try:
validate(instance={"name": "Alice", "age": 30}, schema=schema)
print("OK")
except ValidationError as e:
print("FAIL:", e.message, "at", list(e.absolute_path))
Сбор всех ошибок через Draft202012Validator
validate() бросает исключение на первой ошибке. Чтобы получить сразу полный список (полезно для ответов формы), используйте iter_errors:
from jsonschema import Draft202012Validator
validator = Draft202012Validator(schema)
errors = sorted(validator.iter_errors(instance), key=lambda e: e.path)
for err in errors:
print(f" - {'/'.join(map(str, err.absolute_path))}: {err.message}")
Теперь пользователь исправит всё за один раз вместо круговых обращений туда-обратно.
jsonschema против Pydantic: что когда выбирать
Две сильные библиотеки Python, две разные задачи.
| Параметр | jsonschema | Pydantic v2 |
|---|---|---|
| Формат схемы | Словарь JSON (схема это данные) | Класс Python с аннотациями типов |
| Производительность | Интерпретируется, в ~10–100 раз медленнее Pydantic | Ядро на Rust, самое быстрое в экосистеме |
| Кросс-языковая переносимость | Да (одна и та же схема работает в JS, Go, Rust) | Нет (только Python) |
| Интеграция с FastAPI и нативными моделями | Ручное преобразование | Встроена |
Полный набор ключевых слов Draft 2020-12 ($dynamicRef и др.) | Полный | Частичный |
Практическое правило: jsonschema подходит для кросс-языковых контрактов (OpenAPI, публичные API, webhook), а Pydantic — для внутренних сервисов на Python. Многие команды запускают обе библиотеки сразу: jsonschema на шлюзе для контроля контракта, Pydantic на уровне приложения для типизированной бизнес-логики. Схема остаётся переносимым артефактом, идентичным тому, что вы скормите Ajv.
Валидация в браузере
Зачем вообще валидировать на стороне клиента
Три причины, по убыванию важности. Прежде всего UX: мгновенная обратная связь по мере набора всегда лучше, чем round trip до сервера. Дальше трафик: очевидные ошибки даже не покидают браузер. И, наконец, гигиена безопасности: лишний мусорный объём не доходит до бэкенда. Это не отменяет серверную валидацию, а лишь снимает с неё часть нагрузки.
Никогда не доверяйте только клиентской валидации. На сервере её нужно повторить полностью.
Сборка Ajv для браузера
npm install ajv ajv-formats
import Ajv2020 from "ajv/dist/2020";
import addFormats from "ajv-formats";
const ajv = new Ajv2020({ allErrors: true });
addFormats(ajv);
export const validateForm = ajv.compile({
type: "object",
properties: {
email: { type: "string", format: "email" },
password: { type: "string", minLength: 8 }
},
required: ["email", "password"]
});
Бандл прибавляет около 30 КБ в gzip: вес ощутимый, но в большинстве проектов терпимый. Команды берут Ajv, когда хотят держать одно определение схемы для сервера и клиента.
Более лёгкие альтернативы: Zod и Valibot
Если экосистема JSON Schema не нужна, а вы уже на TypeScript, нативный для TS валидатор даст меньший бандл и более точный вывод типов:
import { z } from "zod";
const UserSchema = z.object({
email: z.string().email(),
password: z.string().min(8)
});
const result = UserSchema.safeParse(data);
if (!result.success) console.log(result.error.issues);
Valibot занимает примерно 3 КБ в gzip и предлагает похожий API. Берите его, когда размер бандла важнее всего. Есть подвох: ни одна из этих библиотек не отдаёт JSON Schema наружу. Если вам нужен один источник истины для бэкенда, сторонних клиентов или генераторов OpenAPI, оставайтесь на Ajv. А когда вся кодовая база — это ваш собственный TypeScript, Zod и Valibot эргономичнее.
Что добавляет Draft 2020-12
prefixItems для валидации кортежей
Draft 7 выражал кортежи через items: [] плюс additionalItems. Draft 2020-12 разделяет их чисто:
{
"type": "array",
"prefixItems": [
{ "type": "string" },
{ "type": "number" }
],
"items": false
}
["x", 42] проходит. ["x", 42, "extra"] не проходит. Схема читается ровно так, как и работает.
unevaluatedProperties для составных схем
Незаметная проблема, на которую напарывается почти каждая команда с allOf или oneOf: additionalProperties: false действует только на том уровне схемы, где он напрямую объявлен. Соседние подсхемы внутри allOf спокойно объявляют любые свойства. В 2020-12 это решает unevaluatedProperties: false:
{
"allOf": [
{ "$ref": "#/$defs/base" }
],
"unevaluatedProperties": false
}
Тогда схема отвергает любое свойство, которое не было оценено ни одной из ветвей. Именно такого поведения большинство разработчиков и ждёт от additionalProperties: false.
$dynamicRef для рекурсивных схем
Каждый, кто пытался описать рекурсивную схему дерева в Draft 7, знает, на какие ухищрения там приходится идти. $dynamicRef вместе с $dynamicAnchor приводят это в порядок:
{
"$dynamicAnchor": "node",
"type": "object",
"properties": {
"value": { "type": "string" },
"children": { "type": "array", "items": { "$dynamicRef": "#node" } }
}
}
Рекурсия декларативна и переопределяема из потомков без переписывания $id.
Draft 7 против 2020-12: что выбирать
- Новый проект, современный стек → Draft 2020-12
- Создаёте или потребляете OpenAPI 3.1 → 2020-12 это родной диалект
- Работаете с OpenAPI 3.0 или более старыми сервисами → Draft 4 (OpenAPI 3.0 использует подмножество Draft 4; не смешивайте диалекты)
- Нужна широкая совместимость валидаторов (Postman, старые инструменты CI) → Draft 7 всё ещё самый безопасный формат обмена
Все современные валидаторы (Ajv, Python jsonschema, jsonschema-rs, Java networknt/json-schema-validator) сегодня поддерживают 2020-12.
Шаблоны из реальной жизни
Валидация ввода API
Express middleware выше — это рабочая форма для production. Поверх неё стоит закрепить две практики. Держите все схемы в каталоге schemas/ в корне репозитория, и добавьте шаг CI, который прогонит ajv test (или эквивалент в Python) и проверит сами схемы по мета-схеме JSON Schema.
Конфигурационные файлы
Visual Studio Code поставляется с интеграцией SchemaStore: автодополнение и встроенная валидация для package.json, tsconfig.json и десятков других. Добавьте поле $schema в собственные конфиги, и пользователи редактора получат то же самое.
Тестовые фикстуры в CI
Тестовые фикстуры стареют. Кто-то обновляет модель, фикстура остаётся в старой форме, тест по-прежнему проходит, потому что утверждения просто не касаются изменившегося поля. Ловите такие случаи проверкой по схеме ещё до того, как запустятся утверждения:
import { glob } from "glob";
const files = await glob("__tests__/fixtures/*.json");
for (const f of files) {
const data = JSON.parse(await fs.readFile(f, "utf8"));
if (!validate(data)) throw new Error(`${f}: ${ajv.errorsText(validate.errors)}`);
}
Когда срабатывает проверка схемы, следующий шаг обычно структурный diff. Прогоните фикстуру в JSON Diff и сравнение против свежей production-выборки, чтобы увидеть, что разъехалось. Если в diff доминируют timestamp и ID, примените шаблоны игнорирования путей в snapshot из гида по JSON diff, чтобы отделить сигнал от шума.
Payload webhook (Stripe, GitHub)
Сторонние webhook — одно из самых ценных мест применения JSON Schema. Webhook это контракт, провайдер может его изменить, и вы хотите узнать об этом в момент изменения, а не неделей позже. Stripe и GitHub публикуют описания OpenAPI, из которых легко извлечь JSON Schema. Валидируйте входящие события: тогда ломающее обновление сработает в ваш мониторинг, а не тихо испортит состояние.
Валидация форм по схеме
У React Hook Form есть адаптер @hookform/resolvers/ajv, у VeeValidate во Vue — аналогичный плагин Ajv. Оба управляют отрисовкой формы, сообщениями об ошибках и валидацией отправки на основе одной JSON Schema. Схема становится единым источником истины, а UI просто наследует её правила.
Дружелюбные сообщения об ошибках
Почему дефолты грубоваты
По умолчанию Ajv выдаёт ошибки вроде #/properties/email format must match "email". Инженеру, который разбирает 400-й ответ, этого достаточно. Пользователю, заполняющему форму оформления заказа, такая строка ничего не скажет.
ajv-errors для пользовательских сообщений
npm install ajv-errors
import ajvErrors from "ajv-errors";
ajvErrors(ajv);
const schema = {
type: "object",
properties: { email: { type: "string", format: "email" } },
required: ["email"],
errorMessage: {
properties: { email: "Введите корректный адрес email" },
required: { email: "Поле email обязательно" }
}
};
Ключевое слово errorMessage остаётся внутри схемы, поэтому правила валидации и текст для пользователя путешествуют вместе.
ajv-i18n для переведённых ошибок
ajv-i18n поставляет переводы стандартных сообщений на 30+ языков. Одной строки при старте достаточно, чтобы валидатор заговорил на испанском, французском, японском или любой другой локали, которую вы обслуживаете. Удобно как fallback, когда ваши errorMessage покрывают не каждое ограничение.
Сопоставление путей схемы с полями формы
У каждой ошибки Ajv есть instancePath вида /users/0/email. Большинство библиотек форм ожидают точечные пути вроде users[0].email. Преобразование умещается в одну строку:
const fieldPath = error.instancePath.replace(/^\//, "").replace(/\//g, ".");
В Python-овском jsonschema эквивалент живёт в error.absolute_path. Соедините через . для того же эффекта.
Пять ловушек, которые проходят валидацию, а потом ломают production
1. format по умолчанию рекомендательный
Без ajv-formats и вызова addFormats(ajv) каждое ключевое слово format превращается в no-op. Схема с {"format": "email"} спокойно принимает "not-an-email". В production пакет форматов нужно ставить всегда.
2. additionalProperties по умолчанию true
Без additionalProperties: false ваша схема принимает любое необъявленное поле. Клиенты могут присылать дополнительные поля, которые полностью обходят валидацию. Сделайте additionalProperties: false дефолтом для входных контрактов и ослабляйте его осознанно там, где это действительно нужно.
3. additionalProperties не композируется
Внутри allOf, oneOf или anyOf additionalProperties: false смотрит только на свойства своего уровня. Соседние подсхемы проскальзывают мимо. Решение в Draft 2020-12 — unevaluatedProperties: false.
4. Удалённый $ref — это риск для production
$ref: "https://example.com/schema.json" заставляет Ajv обращаться по сети при первой компиляции. Получаете задержку, риск DoS, если удалённый хост зависнет, и лишнюю поверхность атаки MITM. Встраивайте все цели $ref в саму схему или загружайте их с диска во время сборки.
5. Сгенерированные схемы расходятся с реальными данными
Инструменты вроде quicktype и typescript-json-schema строят схемы из существующих типов. На выходе обычно слишком разрешающая схема: каждое поле опционально, additionalProperties открыт. Относитесь к сгенерированным схемам как к черновикам, ужимайте их вручную и поднимайте CI, который валидирует реальные production-выборки по схеме (и наоборот), чтобы расхождения всплывали как можно раньше.
Производительность: цифры и эмпирические правила
- Ajv (Node.js): скомпилированные валидаторы выполняют одну проверку заметно меньше чем за микросекунду. Самый быстрый production-grade валидатор JS из доступных сегодня.
jsonschema(Python): интерпретируется и работает в 10–100 раз медленнее Pydantic. Когда это начинает мешать, переключайтесь наfastjsonschema: он генерирует код Python и по скорости подбирается близко к Ajv.- Rust и Go:
jsonschema-rsиxeipuuv/gojsonschemaна уровне шлюза дают ещё в 2–5 раз больше Ajv. - Самый большой выигрыш одной мерой даёт прекомпиляция: вызов
ajv.compile(schema)один раз при загрузке модуля и переиспользование возвращённого валидатора на каждый запрос. Перекомпиляция на каждый запрос режет пропускную способность в 50 раз и сильнее.
Часто задаваемые вопросы
Что такое валидация JSON Schema простыми словами?
Валидация JSON Schema проверяет, следует ли документ JSON заранее заданному контракту. Контракт (схема) сам по себе JSON, в котором объявлены типы, обязательные поля и ограничения. Валидатор читает схему и данные и либо сообщает «прошло», либо показывает пути, на которых проверка не прошла, и почему.
Как проверить JSON по схеме онлайн?
Вставьте схему и данные в playground ajv.js.org или jsonschemavalidator.net и получите мгновенный вердикт. Если JSON выглядит сломанным, сначала приведите его в порядок в Форматировщике JSON; оба работают в браузере, без загрузки на сервер.
Какой валидатор JSON Schema самый быстрый в 2026?
В Node Ajv с прекомпилированными валидаторами проходит одну проверку меньше чем за микросекунду. В Python fastjsonschema генерирует код и достигает класса пропускной способности Ajv. На уровне шлюза jsonschema-rs (Rust) и gojsonschema (Go) в 2–5 раз быстрее Ajv. Любой из этих вариантов нужно компилировать один раз и переиспользовать; иначе все цифры можно делить на 50.
В чём разница между JSON Schema и типами TypeScript?
TypeScript проверяет код, который вы пишете, во время компиляции. JSON Schema проверяет неизвестный JSON во время выполнения. TypeScript не видит JSON, прилетающий из HTTP-ответа, файла или вставки пользователя; именно для этого нужен JSON Schema.
Использовать Draft 2020-12 или Draft 7?
Для новых проектов в 2026 году берите Draft 2020-12. prefixItems, unevaluatedProperties и $dynamicRef закрывают реальные практические задачи. OpenAPI 3.1 нативно использует 2020-12. На Draft 7 имеет смысл оставаться только ради совместимости с Postman или более старыми сервисами. OpenAPI 3.0 опирается на подмножество Draft 4, и смешивать диалекты не стоит.
Как сгенерировать JSON Schema из существующего JSON?
Есть три практичных варианта. Первый — вставить выборки в quicktype.io или jsonschema.net. Второй — запустить npx genson-js либо pip install genson && genson sample.json в командной строке. Третий — написать руками. Автогенерируемые схемы выходят слишком разрешающими (каждое поле опционально, additionalProperties: true), поэтому ужимайте их вручную, прежде чем относиться к ним как к контрактам.
Может ли JSON Schema заменить OpenAPI?
Нет. OpenAPI внутри использует JSON Schema для описания тел запросов и ответов, а уже сверху добавляет пути, схемы безопасности, параметры и URL серверов. Они композируются: вы пишете схемы, ссылаетесь на них из документа OpenAPI и получаете полные контракты API.
JSON Schema — это то же, что JSONPath или jq?
Разные задачи. JSON Schema валидирует структуру («соответствует ли этот JSON контракту?»). JSONPath и jq извлекают значения («имя каждого pod в фазе Running»). Валидируйте схемой; запрашивайте JSONPath или jq.
Почему моя валидация Ajv проходит, а production отвергает данные?
Почти каждый случай объясняется одним из трёх виновников. Забыли ajv-formats, и format: "email" так никогда и не валидировался. Пропустили additionalProperties: false, и лишние клиентские поля проскользнули мимо. Использовали additionalProperties: false внутри allOf или oneOf и обнаружили, что он не композируется; в этом случае переключайтесь на unevaluatedProperties: false.
Можно ли настроить сообщения об ошибках JSON Schema под конечных пользователей?
Да. В Node поставьте ajv-errors, чтобы встроить errorMessage прямо в схему, и ajv-i18n для переводов на 30+ локалей. В Python jsonschema отдаёт полный контекст валидации в каждом объекте ошибки, так что вы спокойно собираете «тип ошибки + путь» в любую копирайтинг-конструкцию, принятую в вашей дизайн-системе.