JSON Schema: What It Is and How to Use It
Learn JSON Schema from scratch. Understand types, constraints, composition keywords, $ref, and see a complete practical example for API validation.
¿Qué es JSON Schema?
JSON Schema es un vocabulario que te permite describir la estructura de los datos JSON. Piensa en ello como un contrato: define qué campos debe tener un documento JSON, qué tipos deben ser esos campos y qué restricciones deben cumplir. Cuando un documento se ajusta a su esquema, es válido. Cuando no lo hace, un validador te dice exactamente qué salió mal.
Esto es importante porque JSON en sí mismo es estructuralmente permisivo. Nada en la especificación JSON impide que una API devuelva una cadena donde esperas un número, o que omita un campo requerido. JSON Schema cierra esa brecha.
Por qué JSON Schema es importante
- Contratos de API. Define exactamente lo que tu API espera y devuelve. Tanto los equipos de cliente como de servidor pueden validar las cargas útiles de forma independiente.
- Validación de datos. Captura datos mal formados en la frontera — en envíos de formularios, cargas útiles de webhooks, archivos de configuración o escrituras en bases de datos.
- Documentación. Un esquema es documentación legible por máquina. Las herramientas pueden generar documentos legibles por humanos, formularios e incluso datos simulados a partir de él.
- Generación de código. Genera interfaces de TypeScript, estructuras de Go o clases de datos de Python directamente desde tu esquema.
Lo básico: Tipo, Propiedades, Requerido
Cada JSON Schema comienza con type. Esto le dice al validador qué tipo de valor esperar en el nivel superior:
{
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "El nombre completo del usuario"
},
"age": {
"type": "integer",
"description": "Edad en años"
}
},
"required": ["name"]
}
Este esquema dice: "Espero un objeto JSON con una propiedad name (cadena, requerida) y una propiedad age opcional (entero)." Cualquier objeto que falte name o proporcione un name que no sea una cadena es inválido.
Todos los tipos de JSON Schema
JSON Schema admite siete tipos primitivos. Aquí está cada uno con un ejemplo de esquema mínimo:
| Tipo | Valores Válidos | Ejemplo de Esquema |
|---|---|---|
string | "hola", "" | {"type": "string"} |
number | 3.14, -1, 0 | {"type": "number"} |
integer | 42, -7 | {"type": "integer"} |
boolean | true, false | {"type": "boolean"} |
null | null | {"type": "null"} |
object | {"key": "value"} | {"type": "object", "properties": {...}} |
array | [1, 2, 3] | {"type": "array", "items": {...}} |
Objetos en Detalle
Usa properties para definir los campos esperados, required para listar los obligatorios y additionalProperties para controlar si se permiten campos adicionales:
{
"type": "object",
"properties": {
"id": { "type": "integer" },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "email"],
"additionalProperties": false
}
Establecer additionalProperties: false rechaza cualquier objeto que contenga claves no listadas en properties. Esto es útil para contratos de API estrictos.
Arreglos en Detalle
Usa items para definir cómo debe lucir cada elemento, y minItems / maxItems para restringir la longitud:
{
"type": "array",
"items": { "type": "string" },
"minItems": 1,
"maxItems": 10,
"uniqueItems": true
}
Esto acepta un arreglo de 1 a 10 cadenas únicas. Un arreglo vacío o un arreglo con duplicados fallaría la validación.
Restricciones y Palabras Clave de Validación
Más allá de los tipos básicos, JSON Schema proporciona restricciones detalladas para cada tipo:
Restricciones de Cadenas
| Palabra Clave | Descripción | Ejemplo |
|---|---|---|
minLength | Número mínimo de caracteres | "minLength": 1 |
maxLength | Número máximo de caracteres | "maxLength": 255 |
pattern | Regex que la cadena debe coincidir | "pattern": "^[A-Z]{2}\\\\d{4}$" |
format | Sugerencia de formato semántico | "format": "email" |
enum | Valores permitidos | "enum": ["activo", "inactivo"] |
Restricciones de Números
| Palabra Clave | Descripción | Ejemplo |
|---|---|---|
minimum | El valor debe ser >= esto | "minimum": 0 |
maximum | El valor debe ser <= esto | "maximum": 100 |
exclusiveMinimum | El valor debe ser > esto | "exclusiveMinimum": 0 |
exclusiveMaximum | El valor debe ser < esto | "exclusiveMaximum": 1000 |
multipleOf | El valor debe ser divisible por esto | "multipleOf": 0.01 |
Combinando Esquemas: allOf, anyOf, oneOf, not
Las palabras clave de composición te permiten construir esquemas complejos a partir de esquemas más simples:
- allOf — Los datos deben ser válidos contra todos los esquemas listados. Se utiliza para combinar múltiples restricciones o extender un esquema base.
- anyOf — Los datos deben ser válidos contra al menos uno de los esquemas listados. Útil para campos que aceptan múltiples formatos.
- oneOf — Los datos deben ser válidos contra exactamente uno de los esquemas listados. Bueno para uniones discriminadas.
- not — Los datos deben no ser válidos contra el esquema dado.
{
"oneOf": [
{
"type": "object",
"properties": {
"type": { "const": "email" },
"address": { "type": "string", "format": "email" }
},
"required": ["type", "address"]
},
{
"type": "object",
"properties": {
"type": { "const": "phone" },
"number": { "type": "string", "pattern": "^\\\\+?[0-9]{7,15}$" }
},
"required": ["type", "number"]
}
]
}
Este esquema acepta ya sea un contacto por correo electrónico o un contacto telefónico, pero no ambos — una forma clara de modelar uniones etiquetadas.
Reutilización con $ref
Los esquemas se vuelven repetitivos rápidamente. La palabra clave $ref te permite hacer referencia a una definición reutilizable:
{
"type": "object",
"properties": {
"billing_address": { "$ref": "#/$defs/address" },
"shipping_address": { "$ref": "#/$defs/address" }
},
"$defs": {
"address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"zip": { "type": "string", "pattern": "^[0-9]{5}(-[0-9]{4})?$" }
},
"required": ["street", "city", "zip"]
}
}
}
Tanto billing_address como shipping_address comparten la misma estructura sin duplicación. La sección $defs (llamada definitions en borradores más antiguos) es el lugar convencional para almacenar esquemas reutilizables.
Ejemplo Completo: Validación de Registro de Usuario
Aquí hay un esquema del mundo real para validar una carga útil de registro de usuario. Utiliza la mayoría de las características que hemos cubierto:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "UserRegistration",
"description": "Esquema para el endpoint POST /api/register",
"type": "object",
"properties": {
"username": {
"type": "string",
"minLength": 3,
"maxLength": 30,
"pattern": "^[a-zA-Z0-9_]+$",
"description": "Nombre de usuario alfanumérico, 3-30 caracteres"
},
"email": {
"type": "string",
"format": "email",
"maxLength": 254
},
"password": {
"type": "string",
"minLength": 8,
"maxLength": 128,
"description": "Al menos 8 caracteres"
},
"age": {
"type": "integer",
"minimum": 13,
"maximum": 150
},
"role": {
"type": "string",
"enum": ["user", "moderator"],
"default": "user"
},
"acceptedTerms": {
"type": "boolean",
"const": true
},
"tags": {
"type": "array",
"items": { "type": "string", "maxLength": 20 },
"maxItems": 5,
"uniqueItems": true
}
},
"required": ["username", "email", "password", "acceptedTerms"],
"additionalProperties": false
}
Observa cómo cada campo tiene restricciones claras y exigibles. Un validador rechazará un nombre de usuario de 2 caracteres, una edad inferior a 13, etiquetas duplicadas o cualquier campo adicional no listado en el esquema.
Inténtalo tú mismo: Pega cualquier JSON y genera un esquema automáticamente con nuestro Generador de JSON Schema.
JSON Schema en el Mundo Real
OpenAPI / Swagger
OpenAPI utiliza JSON Schema (con algunas extensiones) para definir cuerpos de solicitud, formas de respuesta y parámetros de consulta. Cada bloque schema en una especificación de OpenAPI es un JSON Schema. Si escribes documentación de OpenAPI, ya estás escribiendo JSON Schema.
Validación de Formularios
Bibliotecas como react-jsonschema-form y ajv (el validador de JSON Schema más rápido para JavaScript) utilizan esquemas para generar y validar formularios en tiempo de ejecución. Define tu esquema una vez, y tanto el backend como el frontend pueden validar contra las mismas reglas.
Esquemas de Base de Datos
MongoDB admite la validación de JSON Schema a nivel de colección. Puedes establecer un validador $jsonSchema que rechace cualquier documento que no se ajuste a tu esquema al insertar o actualizar.
Archivos de Configuración
VS Code, ESLint y muchas otras herramientas utilizan JSON Schema para validar sus archivos de configuración. ¿Esa autocompletación que obtienes al editar tsconfig.json? Está impulsada por JSON Schema.
Comparación de Versiones de Borrador
JSON Schema ha evolucionado a través de varios borradores. Aquí están los que encontrarás:
| Borrador | Año | Adiciones Clave | Estado |
|---|---|---|---|
| Draft-04 | 2013 | Vocabulario central, $ref, tipos básicos | Legado (todavía ampliamente utilizado) |
| Draft-06 | 2017 | const, contains, propertyNames | Legado |
| Draft-07 | 2018 | if/then/else, readOnly, writeOnly | Ampliamente soportado |
| 2019-09 | 2019 | $defs, unevaluatedProperties, dependentRequired | Soportado por validadores principales |
| 2020-12 | 2020 | prefixItems (reemplaza la validación de tuplas), $ref dinámico | Actual / recomendado |
Para nuevos proyectos, usa 2020-12. Si necesitas la máxima compatibilidad con herramientas existentes, Draft-07 es la opción segura — tiene soporte casi universal en bibliotecas.
Errores Comunes a Evitar
- Olvidar
required. Las propiedades definidas bajopropertiesson opcionales por defecto. Si un campo debe estar presente, inclúyelo enrequired. - Confundir
numbereinteger.numberacepta decimales;integerno. Usaintegerpara IDs, conteos y otros valores enteros. - Sobre-restringir demasiado pronto. Comienza con el esquema mínimo que capture errores reales. Siempre puedes ajustar las restricciones más tarde — aflojarlas es un cambio disruptivo.
- No usar
$ref. Duplicar el mismo sub-esquema en múltiples lugares es una pesadilla de mantenimiento. Extrae estructuras compartidas en$defs. - Ignorar la validación de
format. Por defecto, la mayoría de los validadores tratanformatcomo una anotación, no como una restricción. Necesitas habilitar explícitamente la validación de formato (por ejemplo,ajv.addFormat()o pasando{ validateFormats: true }).
Inténtalo tú mismo: Genera un esquema a partir de cualquier muestra de JSON con nuestro Generador de JSON Schema, luego personaliza las restricciones para que coincidan con tus requisitos.