Validación con JSON Schema: valida JSON en Node, Python y el navegador (2026)
En resumen: JSON Schema es un contrato para datos JSON. Declaras los tipos de cada campo, las claves obligatorias y las restricciones; un validador comprueba si un documento JSON cualquiera cumple ese contrato. Usa Ajv en Node para la validación más veloz, la librería jsonschema en Python cuando necesites esquemas portables, y empaqueta Ajv en el navegador para dar respuesta inmediata en formularios y configuraciones. Para proyectos nuevos en 2026, la versión recomendada es Draft 2020-12.
Esta guía cubre el ejemplo mínimo que funciona, los patrones de punta a punta en los tres entornos, y las trampas reales que provocan bugs del tipo “la validación pasa pero producción rechaza los datos”.
Qué es (y qué no es) JSON Schema
Una definición en una frase
Un JSON Schema es un documento JSON que describe la forma de otros documentos JSON. Un validador lee el esquema y los datos, confirma si cumplen, o devuelve las rutas que fallan.
El ejemplo útil más pequeño:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": { "name": { "type": "string" } },
"required": ["name"]
}
{"name": "Alice"} pasa. {"age": 30} falla (falta name). {"name": 42} falla (name no es string). Ese es todo el modelo mental.
JSON Schema vs validación de sintaxis JSON
Son dos problemas distintos que a menudo se confunden.
| Dimensión | Comprobación de sintaxis JSON | Validación con JSON Schema |
|---|---|---|
| Qué comprueba | ¿Es este un documento JSON legal? | ¿Coincide este JSON con el contrato? |
| Detecta | Comas faltantes, comillas simples, comentarios | Tipos incorrectos, campos requeridos ausentes, valores fuera de rango |
| Herramientas | JSON.parse(), Formateador JSON | Ajv, jsonschema (Python), fastjsonschema |
| Cuándo recurrir a ella | Lo primero, antes de parsear | Justo después de parsear, antes de la lógica de negocio |
En la práctica haces las dos cosas. Imprime con formato el payload en el Formateador JSON para confirmar que parsea; después pásalo por un esquema para verificar el contrato.
JSON Schema vs JSONPath, JSON Patch, jq y TypeScript
Cinco herramientas comparten este espacio. Matriz de decisión:
| Herramienta | Pregunta que responde | Recurre a ella cuando |
|---|---|---|
| JSON Schema | ¿Coincide este JSON con la estructura esperada? | Validas entradas de API, archivos de configuración o payloads de formularios |
| JSONPath | ¿Cómo extraigo un valor de este JSON? | Necesitas leer campos anidados o lecturas en lote |
| JSON Patch (RFC 6902) | ¿Cómo describo el diff de A a B? | Editas en colaboración o sincronizas de forma incremental |
| jq | ¿Cómo proceso JSON desde la línea de comandos? | Scripts de shell, pipelines de logs, comprobaciones de CI |
| Tipos de TypeScript | ¿Mi código usa esta forma correctamente? | Necesitas garantías en tiempo de compilación dentro de un mismo código |
La distinción que importa: JSON Schema valida datos desconocidos en tiempo de ejecución, mientras que TypeScript valida código conocido en tiempo de compilación. TypeScript no puede ayudarte con el JSON que llega de un webhook de terceros o que un usuario pega en un campo; para eso está JSON Schema. Zod y Pydantic ocupan un terreno intermedio (tipos en compilación, más validación en ejecución), y los veremos más abajo.
JSON Schema vs OpenAPI
Una idea equivocada habitual es que OpenAPI reemplaza a JSON Schema. No lo hace. OpenAPI usa JSON Schema internamente para describir los cuerpos de petición y respuesta, y encima añade rutas, parámetros, esquemas de seguridad y URLs de servidor. El esquema es el contrato de la forma de los datos; OpenAPI es el contrato de la API que lo envuelve.
| Dimensión | JSON Schema | OpenAPI |
|---|---|---|
| Alcance | Forma de un único documento JSON | Forma de una API HTTP completa |
| Dependencias | Ninguna (el esquema es JSON autocontenido) | Importa JSON Schema para definir cuerpos |
| Versiones emparejadas | Draft 7 / Draft 2019-09 / Draft 2020-12 | OpenAPI 3.0 usa un subconjunto de Draft 4; OpenAPI 3.1 usa Draft 2020-12 de forma nativa |
| Uso típico | Archivos de configuración, sobres de mensajes, validación de formularios, contratos de un solo payload | Diseño de API REST, generación de SDK, servidores mock, contract testing |
| Generación de código | Limitada (algunas herramientas estilo quicktype) | Ecosistema maduro (openapi-generator, oapi-codegen, SDKs de proveedores) |
| Gestión de contratos | Un archivo por forma, sin enrutamiento | Rutas, operaciones, flujos de auth y endpoints versionados en un solo documento |
Tira de JSON Schema a secas cuando el artefacto que importa es un único documento: un payload de webhook, un archivo de configuración, un mensaje de cola o un formulario. No hay superficie HTTP que describir, así que OpenAPI es sobrecarga.
Tira de OpenAPI cuando publicas una API HTTP y quieres que un solo documento alimente la documentación, la generación de SDK, los servidores mock y los contract tests. Define primero los esquemas como archivos JSON Schema independientes en un directorio schemas/ y luego haz $ref a ellos desde el documento OpenAPI. Así los esquemas siguen siendo reutilizables fuera del contexto de la API.
El emparejamiento de versiones suele despistar a los equipos. OpenAPI 3.0 usa un subconjunto de Draft 4, así que no puedes usar palabras clave de Draft 2020-12 como prefixItems o unevaluatedProperties dentro de un documento 3.0; los generadores las ignorarán en silencio. OpenAPI 3.1 es un superconjunto de Draft 2020-12, así que cualquier cosa válida en 2020-12 lo es en 3.1. Si puedes elegir, apunta a OpenAPI 3.1 y escribe esquemas Draft 2020-12 en todas partes.
Tu primer JSON Schema (5 minutos)
Las palabras clave que necesitas primero
Con estas cubres el 80% del camino:
{
"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
}
El vocabulario:
type—string,number,integer,boolean,null,array,objectproperties+required— declara los campos y marca cuáles deben estar presentesenum/const— restringe a un conjunto fijo o a un literal únicominimum/maximum/multipleOf— límites numéricosminLength/maxLength/pattern— longitud de cadena y regexminItems/maxItems/uniqueItems— forma del arrayadditionalProperties: false— rechaza claves no declaradas (ponlo siempre en contratos de entrada)
Ejemplos de JSON Schema por caso de uso
Las palabras clave anteriores aparecen en distintas combinaciones según lo que estés validando. Algunas formas representativas:
Cuerpo de petición de API — un endpoint de registro que acepta email y contraseña:
{
"type": "object",
"properties": {
"email": { "type": "string", "format": "email" },
"password": { "type": "string", "minLength": 8, "maxLength": 128 }
},
"required": ["email", "password"],
"additionalProperties": false
}
Archivo de configuración — config de un logger que fija el nivel a un conjunto cerrado:
{
"type": "object",
"properties": {
"level": { "enum": ["debug", "info", "warn", "error"] },
"output": { "type": "string", "default": "stdout" }
},
"required": ["level"],
"additionalProperties": false
}
Payload de formulario con reglas condicionales — cuando accountType es "business", taxId pasa a ser obligatorio:
{
"type": "object",
"properties": {
"accountType": { "enum": ["personal", "business"] },
"taxId": { "type": "string" }
},
"if": { "properties": { "accountType": { "const": "business" } } },
"then": { "required": ["taxId"] }
}
Registro JSON de fila CSV — una fila de una tabla de pedidos exportada:
{
"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"]
}
Sobre de evento de webhook — oneOf discrimina por el literal type, así que cada variante de evento tiene su propia forma de payload:
{
"oneOf": [
{ "properties": { "type": { "const": "order.created" }, "data": { "$ref": "#/$defs/order" } } },
{ "properties": { "type": { "const": "order.refunded" }, "data": { "$ref": "#/$defs/refund" } } }
]
}
Estos cinco ejemplos cubren la mayor parte de lo que los equipos escriben en la práctica. Copia el más cercano y ajusta los nombres de los campos; el vocabulario de palabras clave se mantiene igual.
Validar sin instalar nada
Pega el esquema y el payload en el playground de ajv.js.org o en jsonschemavalidator.net, y tendrás un resultado al instante. Si el JSON ya parece sospechoso, pásalo primero por el Formateador JSON.
Validación en Node.js con Ajv
Instalación y un ejemplo de 12 líneas
En la primera llamada a compile, Ajv convierte tu esquema en una función optimizada y luego la reutiliza.
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");
Cambiar a Draft 2020-12
El constructor Ajv por defecto sigue anclado a Draft 7 por compatibilidad hacia atrás. Hay que activar 2020-12 de forma explícita:
import Ajv2020 from "ajv/dist/2020";
const ajv = new Ajv2020({ strict: true, allErrors: true });
Ahora prefixItems, unevaluatedProperties y $dynamicRef están disponibles. Más abajo, en la sección de Draft 2020-12, explicamos qué hace cada uno.
Activar la validación de format
Este detalle de Ajv toma por sorpresa a casi todo el mundo la primera vez: format: "email" no hace nada por defecto. La especificación trata format como informativo, así que hay que registrar el módulo de formatos:
npm install ajv-formats
import addFormats from "ajv-formats";
addFormats(ajv); // ahora "format": "email" sí valida
Sin este paso, {"email": "not-an-email"} pasa un esquema que pide format: "email". En producción, instala siempre ajv-formats.
Middleware de Express en serio
Un validador por ruta, compilado al arrancar:
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 });
}
// ... lógica de negocio
res.status(201).json({ ok: true });
});
El error más caro: llamar a ajv.compile(schema) dentro del manejador de la petición. Compila una sola vez en el ámbito del módulo y reutiliza la función devuelta. Recompilar en cada petición hunde el throughput 50× o más.
Validación en Python con jsonschema
Instalación y uso básico
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))
Recoger todos los errores con Draft202012Validator
validate() lanza una excepción al primer error. Para listar todos los problemas de una vez (útil en respuestas de formulario), usa 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}")
Así el usuario corrige todo de una sola vez, en lugar de ir y venir entre el formulario y el servidor.
jsonschema vs Pydantic: cuándo elegir cuál
Dos librerías sólidas en Python, dos problemas distintos.
| Dimensión | jsonschema | Pydantic v2 |
|---|---|---|
| Formato del esquema | Un dict de JSON (el esquema es dato) | Una clase de Python con type hints |
| Rendimiento | Interpretado, ~10–100× más lento que Pydantic | Núcleo en Rust, el más rápido del ecosistema |
| Portabilidad entre lenguajes | Sí (el mismo esquema funciona en JS, Go, Rust) | No (solo Python) |
| Integración con FastAPI / modelos nativos | Conversión manual | Integrado |
Soporte completo de palabras clave Draft 2020-12 ($dynamicRef, etc.) | Completo | Parcial |
La regla que aguanta en producción: usa jsonschema para contratos entre lenguajes (OpenAPI, APIs públicas, webhooks) y usa Pydantic para servicios internos de Python. Muchos equipos usan las dos a la vez: jsonschema en el gateway para hacer cumplir el contrato, y Pydantic en la capa de aplicación para la lógica de negocio tipada. El esquema es el artefacto portable, idéntico al que le pasarías a Ajv.
Validación en el navegador
Por qué validar en el cliente
Tres razones, en orden de importancia:
- UX: la respuesta inmediata mientras el usuario escribe es mucho mejor que esperar al servidor.
- Ancho de banda: los errores obvios nunca salen del navegador.
- Higiene de seguridad: recorta el volumen de basura que llega al backend, aunque no sustituye la validación en el servidor.
Nunca te quedes solo con la validación del cliente. Hay que volver a validar en el servidor.
Empaquetar Ajv para el navegador
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"]
});
El bundle añade unos 30 KB gzipeados. Es un peso a tener en cuenta, pero no es catastrófico. Los equipos eligen Ajv cuando quieren una sola definición de esquema compartida entre servidor y cliente.
Alternativas más ligeras: Zod y Valibot
Si no necesitas el ecosistema de JSON Schema y ya estás en TypeScript, un validador nativo de TS te da bundles más pequeños e inferencia de tipos más estricta:
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 pesa unos 3 KB gzipeados y tiene una API parecida. Elígelo cuando el tamaño del bundle te importe más que todo lo demás. La trampa está en que ninguna de las dos librerías produce JSON Schema. Si necesitas una sola fuente de verdad compartida con backends, clientes de terceros o generadores de OpenAPI, quédate con Ajv. Si todo es tu propio TypeScript, Zod y Valibot resultan más ergonómicos.
Qué aporta Draft 2020-12
prefixItems para validación de tuplas
Draft 7 expresaba las tuplas con items: [] más additionalItems. Draft 2020-12 las separa de forma más limpia:
{
"type": "array",
"prefixItems": [
{ "type": "string" },
{ "type": "number" }
],
"items": false
}
["x", 42] pasa. ["x", 42, "extra"] falla. El esquema se lee igual que funciona.
unevaluatedProperties para esquemas compuestos
Hay un bug sutil que termina mordiendo a todo equipo que usa allOf o oneOf: additionalProperties: false solo comprueba el nivel inmediato en el que aparece. Los subesquemas hermanos dentro de allOf pueden declarar las propiedades que quieran. La solución de 2020-12 se llama unevaluatedProperties: false:
{
"allOf": [
{ "$ref": "#/$defs/base" }
],
"unevaluatedProperties": false
}
Esto rechaza cualquier propiedad que ninguna rama haya evaluado, que es el comportamiento que la mayoría de la gente espera de additionalProperties: false.
$dynamicRef para esquemas recursivos
Quien haya intentado declarar un esquema de árbol recursivo en Draft 7 conoce las contorsiones que hacen falta. $dynamicRef junto con $dynamicAnchor lo simplifica:
{
"$dynamicAnchor": "node",
"type": "object",
"properties": {
"value": { "type": "string" },
"children": { "type": "array", "items": { "$dynamicRef": "#node" } }
}
}
La recursión queda declarativa y los descendientes pueden sobreescribirla sin reescribir el $id.
Draft 7 vs 2020-12: cuál elegir
- Proyecto nuevo con toolchain moderno: Draft 2020-12.
- Construyes o consumes OpenAPI 3.1: 2020-12 es el dialecto nativo.
- Trabajas con OpenAPI 3.0 o servicios más antiguos: Draft 4 (OpenAPI 3.0 usa un subconjunto de Draft 4; no mezcles dialectos).
- Necesitas compatibilidad amplia con validadores (Postman, herramientas de CI viejas): Draft 7 sigue siendo el formato de intercambio más seguro.
Todos los validadores modernos (Ajv, jsonschema de Python, jsonschema-rs, networknt/json-schema-validator de Java) ya soportan 2020-12 hoy.
Patrones del mundo real
Validación de entradas de API
El middleware de Express de arriba es el patrón de producción. Dos prácticas encima de eso: guarda todos los esquemas en un directorio schemas/ en la raíz del repositorio, y añade un paso de CI que ejecute ajv test (o el equivalente de Python) para validar los propios esquemas contra el meta-esquema de JSON Schema.
Archivos de configuración
Visual Studio Code viene con integración de SchemaStore, o sea, autocompletado y validación inline para package.json, tsconfig.json y decenas más. Añade un campo $schema a tus propias configuraciones y los usuarios del editor reciben el mismo trato sin esfuerzo extra.
Fixtures de test en CI
Los fixtures de test envejecen mal. Alguien actualiza un modelo, un fixture se queda con la forma vieja, y el test sigue pasando porque las aserciones jamás tocaron el campo que cambió. Atrapa eso con una comprobación de esquema antes de que corran las aserciones:
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)}`);
}
Cuando salta la comprobación de esquema, el siguiente paso suele ser un diff estructural. Lleva el fixture al Comparar JSON frente a una muestra fresca de producción y verás qué se desvió. Si los timestamps y los IDs dominan el diff, aplica los patrones de path-ignore para snapshots de la guía de JSON diff, así separas la señal del ruido.
Payloads de webhooks (Stripe, GitHub)
Los webhooks de terceros son uno de los lugares donde JSON Schema da más rendimiento. El webhook es un contrato, el proveedor puede cambiarlo, y tú quieres enterarte en el mismo momento en que pase. Stripe y GitHub publican descripciones de OpenAPI de las que puedes extraer JSON Schemas. Valida los eventos entrantes y, cuando llegue un cambio incompatible, salta tu monitoreo en lugar de corromper el estado en silencio.
Validación de formularios guiada por esquema
React Hook Form trae un adaptador @hookform/resolvers/ajv; VeeValidate de Vue tiene un plugin equivalente para Ajv. Los dos manejan el render del formulario, los mensajes de error y la validación al enviar a partir de un solo JSON Schema. El esquema es la única fuente de verdad y la UI hereda sus reglas.
Mensajes de error pensados para personas
Por qué los predeterminados se quedan cortos
De entrada, Ajv produce errores como #/properties/email format must match "email". Sirve para un ingeniero depurando un 400, pero no le dice nada útil a un usuario rellenando un formulario de checkout.
ajv-errors para mensajes personalizados
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: "Por favor, ingresa una dirección de correo válida" },
required: { email: "El correo es obligatorio" }
}
};
La palabra clave errorMessage se queda dentro del esquema, así que las reglas de validación y los textos para el usuario viajan juntos en un solo archivo.
ajv-i18n para errores traducidos
ajv-i18n trae traducciones de los mensajes por defecto en más de 30 idiomas. Una línea al arrancar y tu validador habla español, francés, japonés o las locales que tú sirvas. Va bien como respaldo cuando tus overrides de errorMessage no cubren todas las restricciones.
Mapear rutas del esquema a campos del formulario
Cada error de Ajv tiene un instancePath del tipo /users/0/email. La mayoría de las librerías de formularios esperan rutas con puntos como users[0].email. Se resuelve en una línea:
const fieldPath = error.instancePath.replace(/^\//, "").replace(/\//g, ".");
En jsonschema de Python, el equivalente vive en error.absolute_path. Únelo con . para el mismo efecto.
Cinco trampas que pasan la validación y luego revientan en producción
1. format es informativo por defecto
Sin ajv-formats y la llamada addFormats(ajv), toda palabra clave format es un no-op. {"format": "email"} acepta sin problema "not-an-email". En producción, instala siempre el paquete de formatos.
2. additionalProperties por defecto es true
Sin additionalProperties: false, tu esquema acepta cualquier campo no declarado. Los clientes pueden enviar campos extra que se saltan la validación por completo. Pon additionalProperties: false como predeterminado en los contratos de entrada y relájalo a propósito cuando lo necesites.
3. additionalProperties no se compone
Dentro de allOf, oneOf o anyOf, additionalProperties: false solo inspecciona las propiedades de su propio nivel. Los subesquemas hermanos se cuelan sin que te enteres. La solución de Draft 2020-12 se llama unevaluatedProperties: false.
4. Los $ref remotos son un riesgo en producción
$ref: "https://example.com/schema.json" hace que Ajv lance un fetch por red en el primer compile. Eso significa latencia, exposición a DoS si el host remoto se cuelga, y una superficie de ataque MITM. Inlinea todos los destinos de $ref o cárgalos desde disco en tiempo de build.
5. Los esquemas generados se desvían de los datos reales
Herramientas como quicktype y typescript-json-schema generan esquemas a partir de tipos existentes. La salida suele ser muy permisiva: todo campo opcional, additionalProperties abierto. Trata los esquemas generados como borradores, ajústalos a mano, y corre en CI una validación de muestras reales de producción contra el esquema (y al revés), de modo que la deriva salga a la luz rápido.
Rendimiento: números y reglas prácticas
- Ajv (Node.js): los validadores compilados resuelven una comprobación en bastante menos de un microsegundo. Es el validador JS de calidad de producción más rápido que hay disponible.
jsonschema(Python): interpretado, entre 10 y 100 veces más lento que Pydantic. Cuando esto te empiece a doler, cambia afastjsonschema, que genera código Python y se acerca a Ajv.- Rust y Go:
jsonschema-rsyxeipuuv/gojsonschemate dan otros 2 a 5× sobre Ajv en la capa del gateway. - El mayor ahorro absoluto: precompila. Llama a
ajv.compile(schema)una vez al cargar el módulo y reutiliza el validador devuelto en cada petición. Recompilar en cada petición mata el throughput 50× o más.
Preguntas frecuentes
¿Qué es la validación con JSON Schema en lenguaje sencillo?
La validación con JSON Schema comprueba si un documento JSON cumple un contrato. El contrato (el esquema) es a su vez JSON, y declara tipos, campos obligatorios y restricciones. Un validador lee el esquema y los datos, y reporta “pasa”, o bien las rutas que fallaron y por qué.
¿Cómo valido JSON contra un esquema en línea?
Pega el esquema y los datos en el playground de ajv.js.org o en jsonschemavalidator.net y obtienes un resultado al instante. Si el JSON parece malformado, límpialo primero en el Formateador JSON. Las dos opciones corren en el navegador y nada se sube a un servidor.
¿Qué validador de JSON Schema es el más rápido en 2026?
En Node, Ajv con validadores precompilados resuelve una comprobación en menos de un microsegundo. En Python, fastjsonschema genera código y alcanza un throughput del nivel de Ajv. En la capa del gateway, jsonschema-rs (Rust) y gojsonschema (Go) son entre 2 y 5 veces más rápidos que Ajv. Sea cual sea tu elección, precompila una vez y reutiliza el validador.
¿Cuál es la diferencia entre JSON Schema y los tipos de TypeScript?
TypeScript comprueba el código que escribes en tiempo de compilación. JSON Schema comprueba JSON desconocido en tiempo de ejecución. TypeScript no puede mirar el JSON que llega de una respuesta HTTP, de un archivo o de algo que pega el usuario; para eso está justamente JSON Schema.
¿Debo usar Draft 2020-12 o Draft 7?
Para proyectos nuevos en 2026, elige Draft 2020-12. prefixItems, unevaluatedProperties y $dynamicRef resuelven problemas reales. OpenAPI 3.1 usa 2020-12 de forma nativa. Quédate en Draft 7 solo por compatibilidad con Postman o servicios más antiguos. OpenAPI 3.0 usa un subconjunto de Draft 4, así que no mezcles dialectos.
¿Cómo genero un JSON Schema a partir de JSON existente?
Tienes tres opciones: pegar muestras en quicktype.io o jsonschema.net; correr npx genson-js o pip install genson && genson sample.json en la línea de comandos; o escribirlo a mano. Los esquemas autogenerados son muy permisivos (todo campo opcional, additionalProperties: true), así que ajústalos siempre antes de usarlos como contratos.
¿Puede JSON Schema sustituir a OpenAPI?
No. OpenAPI usa JSON Schema internamente para describir cuerpos de petición y respuesta, y encima añade rutas, esquemas de seguridad, parámetros y URLs de servidor. Se componen: escribes tus esquemas, los referencias desde un documento OpenAPI y obtienes contratos completos de API.
¿JSON Schema es lo mismo que JSONPath o jq?
No, son problemas distintos. JSON Schema valida estructura (“¿coincide este JSON con el contrato?”). JSONPath y jq extraen valores (“el nombre de cada pod en fase Running”). Valida con un esquema; consulta con JSONPath o jq.
¿Por qué mi validación con Ajv pasa pero producción rechaza los datos?
Casi siempre el culpable es uno de tres: olvidar ajv-formats, con lo cual format: "email" nunca validó nada; omitir additionalProperties: false y dejar que campos extra del cliente se cuelen; o usar additionalProperties: false dentro de allOf u oneOf sin saber que ahí no se compone. Cambia entonces a unevaluatedProperties: false.
¿Puedo personalizar los mensajes de error de JSON Schema para usuarios finales?
Sí. En Node, instala ajv-errors para incrustar errorMessage dentro del esquema, y ajv-i18n para traducciones en más de 30 locales. En Python, jsonschema expone el contexto completo de validación en cada objeto de error, así que puedes mapear el tipo de error y la ruta a los textos que use tu sistema de diseño.