modulo 08 / lezione 40
Debugging di dati, backend e integrazioni
Contratti API e schema dei dati
Controllare se richiesta, risposta e modello dati concordano su nomi, tipi, valori mancanti, formati e versione dell'API.
In parole semplici
Tra client e server c’è un contratto: nomi dei campi, tipi, valori opzionali, formati delle date, semantica dei codici. Quando il contratto è scritto, condiviso e verificato, le integrazioni sono noiose. Quando è implicito, ogni cambiamento è un bug latente.
Parole nuove
- contratto API: accordo esplicito tra producer e consumer di un’API.
- schema: descrizione formale della struttura dei dati (JSON Schema, OpenAPI, GraphQL SDL).
- breaking change: modifica che rompe i client esistenti.
- versioning: meccanismo per evolvere l’API senza rompere.
Cosa impari
- Confrontare richiesta inviata, risposta ricevuta e modello dati atteso.
- Riconoscere contratti rotti silenziosamente.
- Usare uno schema (OpenAPI/JSON Schema) per validare.
Tre punti di confronto
[1. CLIENT richiesta] → [2. SERVER risposta] → [3. CLIENT modello]
Ogni transizione può divergere:
- Client → richiesta: il client costruisce header/body sbagliati.
- Server → risposta: il server cambia struttura senza preavviso.
- Risposta → modello: il client legge campi che non esistono o ignora campi che ci sono.
Esempio: una card che mostra vuoti
UI mostra “Nome: ” senza valore. Risposta API:
{
"user_name": "Carlo",
"user_age": 35,
"is_active": true
}
Codice client:
function rendiCard(utente) {
return `<div>Nome: ${utente.nome}</div>`; // utente.nome non esiste
}
Bug: la risposta usa user_name (snake_case), il client legge nome. Nessun errore JS: l’accesso a undefined ritorna undefined, che template literal mostra come stringa vuota.
Tre check per trovare il bug
- DevTools → Network → Response: cosa arriva davvero?
{ "user_name": "Carlo", ... }
- Codice client: cosa cerco?
utente.nome
- Documentazione/schema: cosa è stato concordato?
properties:
user_name: { type: string }
...
Discrepanza: cliente vuole nome, contratto dice user_name. Bug nel client (o contratto da aggiornare, se la convenzione era camelCase).
Schema come fonte di verità
OpenAPI (REST)
paths:
/utenti/{id}:
get:
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/Utente'
components:
schemas:
Utente:
type: object
required: [id, user_name, is_active]
properties:
id: { type: integer }
user_name: { type: string }
user_age: { type: integer, minimum: 0 }
is_active: { type: boolean }
Con OpenAPI:
- Documenti il contratto in modo formale.
- Generi client typed (TypeScript, Java, …) automaticamente.
- Validi richieste e risposte in CI.
JSON Schema (validazione runtime)
Libreria ajv in JS:
import Ajv from "ajv";
const ajv = new Ajv();
const schema = {
type: "object",
required: ["user_name", "is_active"],
properties: {
user_name: { type: "string" },
user_age: { type: "integer", minimum: 0 },
is_active: { type: "boolean" },
},
};
const valida = ajv.compile(schema);
if (!valida(dato)) console.error(valida.errors);
Aggiungi validazione al confine: ogni dato che entra dall’esterno passa il vaglio.
GraphQL SDL
In GraphQL il contratto è lo schema. Il client introspeziona, il server lo serve. Vantaggi: nessuna ambiguità. Svantaggi: complessità di setup.
Versioning
Quando il contratto deve cambiare in modo incompatibile:
- URL versioning:
/v1/utenti,/v2/utenti. - Header versioning:
Accept-Version: 2. - Field versioning: nuovo campo opzionale, vecchio mantenuto.
Regola: i breaking change vanno in nuova versione. I non-breaking (aggiunte) possono restare in versione corrente.
Bug ricorrenti del contratto
- camelCase vs snake_case: vedi sopra.
- Date come stringhe diverse:
"2025-01-15","2025-01-15T00:00:00Z","15/01/2025". - ID come numero vs stringa:
42vs"42".===non funziona. - Campi opzionali assenti vs
null:{ "email": null }vs{}— JSON Schema può distinguere. - Array vs oggetto singolo:
{ "items": [...] }vs{ "items": {...} }quando uno solo. - Enum estesi: server aggiunge un valore nuovo, client non lo gestisce.
Procedura di diagnosi
Davanti a un dato “strano”:
- Cattura la response in Network.
- Apri lo schema documentato (Swagger UI, README).
- Confronta:
- I nomi dei campi corrispondono?
- I tipi corrispondono?
- Tutti i required sono presenti?
- Ci sono campi non documentati?
- Annota la discrepanza nel taccuino.
- Fix: lato client se il contratto è rispettato dal server; lato server se invece il contratto è violato; entrambi se il contratto stesso era ambiguo.
Esercizio guidato
Prendi un’API pubblica (es. GitHub API, OpenWeatherMap, JSONPlaceholder):
- Fai una chiamata e cattura la response.
- Cerca lo schema/documentazione.
- Scrivi un piccolo
JSON Schemaper la response. Convalida conajv. - Simula un cambio del server (modifica manualmente il JSON ricevuto): rinomina un campo. Vedi come
ajvreagisce. - Aggiungi nel tuo codice client la validazione al confine: se non valida, log e fallback.
Errori frequenti del principiante
- “Tanto funziona, il contratto lo sapremo dopo”. Sapremo dopo significa “scopriremo in produzione”.
- Validare solo nel tipo TypeScript. I tipi TS sono compile-time, non runtime. Per dati esterni serve validazione runtime.
- Ignorare campi inattesi. A volte il server aggiunge campi: ok, ma loggali per non perderli di vista.
- Modificare schema senza versionare. Rompi i client esistenti.