Validazione JSON Schema: come validare JSON in Node, Python e nel browser (2026)
In sintesi. JSON Schema è un contratto per i dati JSON: dichiari i tipi dei campi, quali chiavi sono obbligatorie e i vincoli, poi un validatore verifica se un documento JSON rispetta quel contratto. Usa Ajv in Node per la validazione più rapida, la libreria jsonschema in Python per schemi portabili, e includi Ajv nel browser per dare feedback immediato su form e configurazioni. Per i nuovi progetti nel 2026, parti da Draft 2020-12.
Sotto trovi l’esempio funzionante più piccolo, i pattern end-to-end nei tre runtime e le trappole reali che producono i bug del tipo “la validazione passa, ma in produzione i dati vengono rifiutati”.
Cos’è (e cosa non è) JSON Schema
Una definizione in una frase
Uno JSON Schema è un documento JSON che descrive la forma di altri documenti JSON. Un validatore legge lo schema e i dati, poi conferma la conformità o restituisce i percorsi che hanno fallito.
L’esempio utile più piccolo:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": { "name": { "type": "string" } },
"required": ["name"]
}
{"name": "Alice"} passa. {"age": 30} fallisce (manca name). {"name": 42} fallisce (name non è una stringa). Questo è l’intero modello mentale.
JSON Schema vs validazione della sintassi JSON
Due problemi diversi, spesso confusi.
| Dimensione | Controllo sintattico JSON | Validazione JSON Schema |
|---|---|---|
| Cosa controlla | È un documento JSON valido? | Il JSON corrisponde al contratto? |
| Cosa cattura | Virgole mancanti, apici singoli, commenti | Tipi sbagliati, campi obbligatori mancanti, valori fuori intervallo |
| Strumenti | JSON.parse(), Formattatore JSON | Ajv, jsonschema (Python), fastjsonschema |
| Quando ti serve | Per primo, prima del parsing | Subito dopo il parsing, prima della logica di business |
Di solito si fanno entrambi: formatti il payload nel Formattatore JSON per confermare che venga interpretato, poi lo passi a uno schema per verificare che rispetti il contratto.
JSON Schema vs JSONPath, JSON Patch, jq e TypeScript
Cinque strumenti condividono lo stesso spazio di problemi. La matrice decisionale:
| Strumento | Domanda a cui risponde | Quando usarlo |
|---|---|---|
| JSON Schema | Questo JSON corrisponde alla struttura attesa? | Validazione di input API, file di configurazione, payload di form |
| JSONPath | Come estraggo un valore da questo JSON? | Estrazione di campi annidati, letture in batch |
| JSON Patch (RFC 6902) | Come descrivo la differenza fra A e B? | Editing collaborativo, sincronizzazione incrementale |
| jq | Come elaboro JSON dalla riga di comando? | Script di shell, pipeline di log, controlli CI |
| Tipi TypeScript | Il mio codice usa correttamente questa forma? | Garanzie a tempo di compilazione all’interno di un’unica codebase |
La distinzione che conta: JSON Schema valida dati sconosciuti a runtime, TypeScript valida codice noto a tempo di compilazione. TypeScript non può aiutarti con il JSON che arriva da un webhook di terze parti o da un incolla dell’utente; è esattamente per questo che esiste JSON Schema. Zod e Pydantic occupano una via di mezzo (tipi a tempo di compilazione più validazione a runtime), e li trattiamo più avanti.
JSON Schema vs OpenAPI
Un equivoco diffuso è che OpenAPI sostituisca JSON Schema. Non è così. OpenAPI usa JSON Schema al suo interno per descrivere i corpi di richiesta e risposta, poi sopra aggiunge percorsi, parametri, schemi di sicurezza e URL del server. Lo schema è il contratto sulla forma dei dati; OpenAPI è il contratto sull’API che lo avvolge.
| Dimensione | JSON Schema | OpenAPI |
|---|---|---|
| Ambito | Forma di un singolo documento JSON | Forma di un’intera API HTTP |
| Dipendenze | Nessuna (lo schema è JSON autonomo) | Importa JSON Schema per la definizione dei corpi |
| Abbinamento di versione | Draft 7 / Draft 2019-09 / Draft 2020-12 | OpenAPI 3.0 usa un sottoinsieme di Draft 4; OpenAPI 3.1 usa Draft 2020-12 nativamente |
| Uso tipico | File di configurazione, envelope di messaggi, validazione di form, contratti su singolo payload | Design di API REST, generazione di SDK, mock server, contract testing |
| Generazione di codice | Limitata (alcuni strumenti tipo quicktype) | Ecosistema maturo (openapi-generator, oapi-codegen, SDK dei vendor) |
| Gestione del contratto | Un file per forma, nessun routing | Percorsi, operazioni, flussi di autenticazione, endpoint versionati in un unico documento |
Scegli JSON Schema quando l’artefatto che ti interessa è un singolo documento: un payload di webhook, un file di configurazione, un messaggio di coda o un form. Non c’è una superficie HTTP da descrivere, quindi OpenAPI è solo overhead.
Scegli OpenAPI quando pubblichi un’API HTTP e vuoi un solo documento che alimenti documentazione, generazione di SDK, mock server e contract test. Definisci prima i tuoi schemi come file JSON Schema indipendenti in una directory schemas/, poi referenziali con $ref dal documento OpenAPI. Così gli schemi restano riutilizzabili anche fuori dal contesto API.
L’abbinamento di versione fa inciampare i team. OpenAPI 3.0 usa un sottoinsieme di Draft 4, quindi non puoi usare keyword di Draft 2020-12 come prefixItems o unevaluatedProperties dentro un documento 3.0: i generatori le ignorano in silenzio. OpenAPI 3.1 è un sovrainsieme di Draft 2020-12, quindi tutto ciò che è valido in 2020-12 è valido in 3.1. Se puoi scegliere, punta su OpenAPI 3.1 e scrivi schemi Draft 2020-12 ovunque.
Il tuo primo JSON Schema (5 minuti)
Le keyword da imparare per prime
Coprirai l’80% dei casi con queste:
{
"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
}
Il vocabolario:
type:string,number,integer,boolean,null,array,objectpropertiesconrequired: dichiara i campi e marca quelli che devono essere presentienumoconst: vincola a un insieme fisso o a un singolo letteraleminimum,maximum,multipleOf: limiti numericiminLength,maxLength,pattern: lunghezza stringa e regexminItems,maxItems,uniqueItems: forma dell’arrayadditionalProperties: false: rifiuta chiavi non dichiarate (impostalo sempre sui contratti di input)
Esempi di JSON Schema per caso d’uso
Le keyword qui sopra compaiono in combinazioni diverse a seconda di cosa stai validando. Alcune forme rappresentative:
Corpo di richiesta API: un endpoint di registrazione che accetta email e password.
{
"type": "object",
"properties": {
"email": { "type": "string", "format": "email" },
"password": { "type": "string", "minLength": 8, "maxLength": 128 }
},
"required": ["email", "password"],
"additionalProperties": false
}
File di configurazione: una configurazione di logger che vincola il livello a un insieme fisso.
{
"type": "object",
"properties": {
"level": { "enum": ["debug", "info", "warn", "error"] },
"output": { "type": "string", "default": "stdout" }
},
"required": ["level"],
"additionalProperties": false
}
Payload di form con regole condizionali: quando accountType è "business", taxId diventa obbligatorio.
{
"type": "object",
"properties": {
"accountType": { "enum": ["personal", "business"] },
"taxId": { "type": "string" }
},
"if": { "properties": { "accountType": { "const": "business" } } },
"then": { "required": ["taxId"] }
}
Record JSON da riga CSV: una riga di una tabella di ordini esportata.
{
"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"]
}
Envelope di evento webhook: oneOf discrimina sul letterale type, così ogni variante di evento ha la propria forma di payload.
{
"oneOf": [
{ "properties": { "type": { "const": "order.created" }, "data": { "$ref": "#/$defs/order" } } },
{ "properties": { "type": { "const": "order.refunded" }, "data": { "$ref": "#/$defs/refund" } } }
]
}
Questi cinque esempi coprono la gran parte di ciò che i team scrivono in pratica. Copia il caso più vicino e adatta i nomi dei campi: il vocabolario delle keyword resta lo stesso.
Validare senza installare nulla
Incolla schema e payload nel playground di ajv.js.org o jsonschemavalidator.net per un verdetto immediato. Se il JSON stesso sembra sospetto, passalo prima nel Formattatore JSON.
Validare in Node.js con Ajv
Installazione ed esempio in 12 righe
Ajv compila il tuo schema in una funzione ottimizzata alla prima compile, poi la riutilizza.
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");
Passare a Draft 2020-12
Il costruttore Ajv di default è ancora fissato a Draft 7 per compatibilità con il passato. Scegli 2020-12 in modo esplicito:
import Ajv2020 from "ajv/dist/2020";
const ajv = new Ajv2020({ strict: true, allErrors: true });
Ora prefixItems, unevaluatedProperties e $dynamicRef sono disponibili. Più avanti, nella sezione su Draft 2020-12, trovi cosa fa ciascuno.
Attivare la validazione di format
Questa è la stranezza di Ajv che inciampa più sviluppatori: format: "email" non fa nulla di default. La specifica considera format come informativo, quindi devi registrare il modulo dei format:
npm install ajv-formats
import addFormats from "ajv-formats";
addFormats(ajv); // ora "format": "email" valida davvero
Senza questo passaggio, {"email": "not-an-email"} passa uno schema che richiede format: "email". Installa sempre ajv-formats in produzione.
Middleware Express sul campo
Un validatore per route, compilato all’avvio:
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 });
}
// ... logica di business
res.status(201).json({ ok: true });
});
L’errore singolo più costoso: chiamare ajv.compile(schema) dentro l’handler della richiesta. Compila una volta sola a livello di modulo, poi riutilizza la funzione restituita. Ricompilare a ogni richiesta abbatte il throughput di 50 volte o più.
Validare in Python con jsonschema
Installazione e uso di base
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))
Raccogliere tutti gli errori con Draft202012Validator
validate() solleva un’eccezione al primo errore. Per elencare ogni problema in una volta sola (utile per le risposte dei form), 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}")
Ora l’utente corregge tutto in una sola volta, invece di rimbalzare avanti e indietro col server.
jsonschema vs Pydantic: quando scegliere quale
Due buone librerie Python per due problemi diversi.
| Dimensione | jsonschema | Pydantic v2 |
|---|---|---|
| Formato dello schema | Un dizionario JSON (lo schema è dato) | Una classe Python con type hint |
| Performance | Interpretato, ~10–100× più lento di Pydantic | Core in Rust, il più veloce dell’ecosistema |
| Portabilità cross-language | Sì (lo stesso schema funziona in JS, Go, Rust) | No (solo Python) |
| Integrazione FastAPI / modelli nativi | Conversione manuale | Integrata |
Keyword complete di Draft 2020-12 ($dynamicRef, ecc.) | Completa | Parziale |
La regola che tiene in produzione: usa jsonschema per i contratti cross-language (OpenAPI, API pubbliche, webhook), usa Pydantic per i servizi Python interni. Molti team mettono in campo entrambi: jsonschema al gateway per applicare il contratto, Pydantic a livello applicativo per la logica di business tipata. Lo schema rimane l’artefatto portabile, lo stesso che daresti in pasto ad Ajv.
Validare nel browser
Perché validare lato client
In ordine di importanza:
- UX: il feedback istantaneo mentre l’utente scrive batte qualunque round trip al server.
- Banda: gli errori evidenti non lasciano mai il browser.
- Igiene di sicurezza: riduce il volume di spazzatura che arriva al backend, anche se non sostituisce la validazione lato server.
Non fidarti mai della sola validazione lato client. Rivalida sempre sul server.
Includere Ajv nel bundle del browser
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"]
});
Il bundle aggiunge circa 30 KB gzippati: non è poco, ma non è catastrofico. Si sceglie Ajv quando si vuole una sola definizione di schema condivisa fra server e client.
Alternative più leggere: Zod e Valibot
Se non ti serve l’ecosistema JSON Schema e sei già in TypeScript, un validatore TS-native ti dà bundle più piccoli e inferenza dei tipi più stretta:
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 circa 3 KB gzippati con un’API simile. Sceglilo quando il peso del bundle è la cosa che pesa di più. Il rovescio della medaglia: nessuna delle due librerie produce JSON Schema. Se ti serve un’unica fonte di verità condivisa con backend, client di terze parti o generatori OpenAPI, resta su Ajv. Se invece tutto si gioca dentro il tuo TypeScript, Zod e Valibot sono più ergonomici.
Cosa aggiunge Draft 2020-12
prefixItems per la validazione delle tuple
Draft 7 esprimeva le tuple tramite items: [] più additionalItems. Draft 2020-12 le separa in modo netto:
{
"type": "array",
"prefixItems": [
{ "type": "string" },
{ "type": "number" }
],
"items": false
}
["x", 42] passa. ["x", 42, "extra"] fallisce. Lo schema si legge esattamente per quello che fa.
unevaluatedProperties per gli schemi composti
C’è un bug sottile che colpisce qualunque team usi allOf o oneOf: additionalProperties: false controlla solo il livello immediato in cui appare. I sottoschemi gemelli dentro allOf dichiarano qualunque proprietà gli pare. La risposta di 2020-12 è unevaluatedProperties: false:
{
"allOf": [
{ "$ref": "#/$defs/base" }
],
"unevaluatedProperties": false
}
Così vengono rifiutate tutte le proprietà che nessuna branch ha valutato: di fatto il comportamento che la maggior parte degli sviluppatori si aspettava già da additionalProperties: false.
$dynamicRef per gli schemi ricorsivi
Chiunque abbia provato a dichiarare uno schema ad albero ricorsivo in Draft 7 conosce le contorsioni che servono. $dynamicRef insieme a $dynamicAnchor mette tutto in chiaro:
{
"$dynamicAnchor": "node",
"type": "object",
"properties": {
"value": { "type": "string" },
"children": { "type": "array", "items": { "$dynamicRef": "#node" } }
}
}
La ricorsione è dichiarativa e i discendenti possono sovrascriverla senza riscrivere $id.
Draft 7 vs 2020-12: quale scegliere
- Nuovo progetto con toolchain moderna: Draft 2020-12.
- Costruisci o consumi OpenAPI 3.1: 2020-12 è il dialetto nativo.
- Lavori con OpenAPI 3.0 o servizi più vecchi: Draft 4 (OpenAPI 3.0 usa un sottoinsieme di Draft 4; non mescolare i dialetti).
- Ti serve compatibilità ampia con i validatori (Postman, vecchi strumenti CI): Draft 7 resta il formato di interscambio più sicuro.
Oggi qualsiasi validatore moderno supporta 2020-12: Ajv, jsonschema per Python, jsonschema-rs, networknt/json-schema-validator di Java.
Pattern reali
Validazione dell’input di un’API
Il middleware Express visto sopra è la forma adatta alla produzione. Due abitudini da affiancargli: tieni tutti gli schemi in una directory schemas/ alla radice del repository, e aggiungi un passo CI che esegue ajv test (o l’equivalente Python) per validare gli schemi stessi contro la meta-schema di JSON Schema.
File di configurazione
Visual Studio Code è integrato con SchemaStore: ottieni autocomplete e validazione inline per package.json, tsconfig.json e decine di altri file. Aggiungi un campo $schema alle tue configurazioni e chi le apre nell’editor riceve lo stesso trattamento.
Fixture di test in CI
Le fixture di test marciscono. Qualcuno aggiorna un modello, una fixture resta com’era, il test continua a passare perché le asserzioni non avevano mai toccato il campo modificato. Per intercettarlo basta un controllo di schema prima delle asserzioni:
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)}`);
}
Quando il controllo di schema scatta, la mossa successiva è quasi sempre un diff strutturale. Porta la fixture in Confronta JSON contro un campione fresco di produzione per vedere cosa è andato alla deriva. Se timestamp e ID intasano il diff, applica gli schemi di esclusione percorsi della guida al diff JSON per separare il segnale dal rumore.
Payload dei webhook (Stripe, GitHub)
I webhook di terze parti sono uno dei punti dove applicare JSON Schema rende di più. Il webhook è un contratto, il provider può cambiarlo, e tu vuoi saperlo nel momento in cui succede. Stripe e GitHub pubblicano descrizioni OpenAPI da cui puoi estrarre JSON Schema. Valida gli eventi in ingresso, e un aggiornamento incompatibile fa scattare il monitoraggio invece di corrompere lo stato in silenzio.
Validazione di form guidata da schema
React Hook Form ha un adattatore @hookform/resolvers/ajv, e VeeValidate di Vue ha un plugin Ajv equivalente. Entrambi pilotano il rendering del form, i messaggi di errore e la validazione al submit a partire da un solo JSON Schema. Lo schema diventa l’unica fonte di verità, e la UI eredita le sue regole.
Messaggi di errore amichevoli
Perché i default sono ruvidi
Di base, Ajv restituisce errori come #/properties/email format must match "email". Va benissimo per chi sta debuggando un 400. Non serve a niente all’utente che sta compilando un form di checkout.
ajv-errors per messaggi personalizzati
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: "Inserisci un indirizzo email valido" },
required: { email: "L'email è obbligatoria" }
}
};
La keyword errorMessage resta dentro lo schema, così le regole di validazione e il testo per l’utente viaggiano nello stesso file.
ajv-i18n per errori tradotti
ajv-i18n distribuisce traduzioni dei messaggi di default in oltre 30 lingue. Una riga all’avvio e il tuo validatore parla spagnolo, francese, giapponese o qualunque locale ti serva. Funziona da fallback quando le tue sovrascritture errorMessage non coprono ogni vincolo.
Mappare i percorsi dello schema sui campi del form
Ogni errore di Ajv arriva con un instancePath come /users/0/email. La maggior parte delle librerie di form si aspetta percorsi puntati come users[0].email. Una riga sola:
const fieldPath = error.instancePath.replace(/^\//, "").replace(/\//g, ".");
In jsonschema di Python l’equivalente sta in error.absolute_path. Uniscilo con . e ottieni lo stesso effetto.
Cinque trappole che superano la validazione e poi mandano in crash la produzione
1. format è informativo per default
Senza ajv-formats più addFormats(ajv), ogni keyword format è un no-op. {"format": "email"} accetta "not-an-email". Installa sempre il pacchetto dei format in produzione.
2. additionalProperties ha default true
Senza additionalProperties: false, il tuo schema accetta qualunque campo non dichiarato. I client possono spedire campi extra che bypassano del tutto la validazione. Tieni additionalProperties: false come default sui contratti di input, e allenta solo quando hai un motivo preciso.
3. additionalProperties non si compone
Dentro allOf, oneOf o anyOf, additionalProperties: false ispeziona solo le proprietà del proprio livello. I sottoschemi gemelli sgusciano via. La risposta di Draft 2020-12 è unevaluatedProperties: false.
4. $ref remoto è un rischio in produzione
$ref: "https://example.com/schema.json" fa sì che Ajv vada in rete alla prima compilazione. Significa latenza, esposizione a DoS se l’host remoto si blocca, e una superficie d’attacco MITM. Inlinea tutti i target $ref, oppure caricali da disco a build time.
5. Gli schemi generati derivano dai dati reali
Strumenti come quicktype e typescript-json-schema generano schemi a partire da tipi esistenti. L’output è quasi sempre troppo permissivo: tutti i campi opzionali, additionalProperties aperto. Considera gli schemi generati come bozze, restringili a mano, e fai girare in CI un controllo che validi campioni reali di produzione contro lo schema (e viceversa) così la deriva emerge in fretta.
Performance: numeri e regole pratiche
- Ajv (Node.js): i validatori compilati eseguono un controllo ben sotto il microsecondo. È il validatore JS production-grade più veloce in giro.
jsonschema(Python): interpretato, da 10 a 100 volte più lento di Pydantic. Quando pesa davvero, sostituiscilo confastjsonschema, che genera codice Python e si avvicina ad Ajv.- Rust e Go:
jsonschema-rsexeipuuv/gojsonschemati danno altre 2-5 volte rispetto ad Ajv al livello gateway. - Il singolo guadagno più grande è precompilare. Esegui
ajv.compile(schema)una volta al caricamento del modulo, poi riutilizza il validatore restituito a ogni richiesta. Ricompilare a ogni richiesta uccide il throughput di 50 volte o più.
Domande frequenti
Cos’è la validazione JSON Schema spiegata in parole semplici?
La validazione JSON Schema controlla se un documento JSON rispetta un contratto. Il contratto (lo schema) è a sua volta JSON e dichiara tipi, campi obbligatori e vincoli. Un validatore legge lo schema e i dati, poi segnala “passa” oppure i percorsi che hanno fallito e perché.
Come valido JSON contro uno schema online?
Incolla schema e dati nel playground di ajv.js.org o in jsonschemavalidator.net per un verdetto immediato. Se il JSON sembra malformato, sistemalo prima nel Formattatore JSON; entrambi girano nel browser, senza upload.
Quale validatore JSON Schema è il più veloce nel 2026?
In Node, Ajv con validatori precompilati esegue un controllo in meno di un microsecondo. In Python, fastjsonschema genera codice e raggiunge throughput in classe Ajv. Al livello gateway, jsonschema-rs (Rust) e gojsonschema (Go) viaggiano da 2 a 5 volte più veloci di Ajv. Qualunque scelga, precompila una volta e riutilizza.
Qual è la differenza fra JSON Schema e i tipi TypeScript?
TypeScript controlla il codice che scrivi a tempo di compilazione. JSON Schema controlla JSON sconosciuto a runtime. TypeScript non può vedere il JSON che arriva da una risposta HTTP, da un file o da un incolla dell’utente; è esattamente per questo che esiste JSON Schema.
Devo usare Draft 2020-12 o Draft 7?
Per i nuovi progetti nel 2026, scegli Draft 2020-12. prefixItems, unevaluatedProperties e $dynamicRef risolvono problemi reali, e OpenAPI 3.1 usa 2020-12 nativamente. Resta su Draft 7 solo per compatibilità con Postman o servizi più vecchi. OpenAPI 3.0 usa un sottoinsieme di Draft 4: non mescolare dialetti.
Come genero un JSON Schema a partire da JSON esistente?
Hai un paio di opzioni. Incolla i campioni in quicktype.io o jsonschema.net; oppure esegui npx genson-js o pip install genson && genson sample.json da riga di comando; oppure scrivilo a mano. Gli schemi auto-generati sono troppo permissivi (tutti i campi opzionali, additionalProperties: true), quindi restringili sempre prima di trattarli come contratti.
JSON Schema può sostituire OpenAPI?
No. OpenAPI usa JSON Schema internamente per descrivere i corpi di richiesta e risposta, poi aggiunge percorsi, schemi di sicurezza, parametri e URL del server. Si compongono: scrivi i tuoi schemi, referenziali da un documento OpenAPI, ottieni contratti API completi.
JSON Schema è la stessa cosa di JSONPath o jq?
No, sono problemi diversi. JSON Schema valida la struttura (“questo JSON corrisponde al contratto?”). JSONPath e jq estraggono valori (“ogni nome di pod nella fase Running”). Validi con uno schema, interroghi con JSONPath o jq.
Perché la mia validazione Ajv passa ma in produzione i dati vengono rifiutati?
Quasi sempre i colpevoli sono questi: dimenticare ajv-formats (così format: "email" non ha mai davvero validato); omettere additionalProperties: false (e lasciare che campi extra del client passino indisturbati); oppure usare additionalProperties: false dentro allOf o oneOf e scoprire che non si compone. In quest’ultimo caso, passa a unevaluatedProperties: false.
Posso personalizzare i messaggi di errore di JSON Schema per gli utenti finali?
Sì. In Node, installa ajv-errors per incorporare errorMessage dentro lo schema, e ajv-i18n per le traduzioni in oltre 30 locale. In Python, jsonschema espone l’intero contesto di validazione su ogni oggetto errore, così puoi mappare il tipo di errore più il percorso sul testo che usa il tuo design system.