Fetch API in JavaScript: leggere e validare risposte JSON in modo robusto

by Anastasia P.
SHARE
Fetch API in JavaScript: leggere e validare risposte JSON in modo robusto
© Guida-HTML5.it

Introduzione

Quando si usa la Fetch API per fare richieste HTTP, il caso più comune è ricevere una risposta in formato JSON. Sembra semplice: si chiama fetch(), poi response.json() e il gioco è fatto. In realtà, in un’applicazione reale ci sono alcuni aspetti importanti da gestire bene: capire se la risposta è davvero valida, distinguere una risposta HTTP riuscita da una risposta con errore, verificare la struttura dei dati ricevuti e prevenire crash dovuti a campi mancanti.

In questo tutorial vediamo un sotto-argomento molto pratico e spesso sottovalutato: come leggere e validare risposte JSON con Fetch API. È un tema utile perché molte integrazioni con API esterne falliscono non per problemi di rete, ma per dati inattesi, campi mancanti o formati diversi da quelli previsti.

L’obiettivo è costruire un piccolo esempio realistico: recuperare i dettagli di un utente da un’API pubblica, controllare la risposta HTTP, validare la struttura del JSON e gestire eventuali anomalie in modo pulito.

Codice completo

// Esempio: recupero e validazione di un utente da una API JSON
// API di esempio: JSONPlaceholder
// Endpoint: https://jsonplaceholder.typicode.com/users/1

async function getUserProfile(userId) {
  const url = `https://jsonplaceholder.typicode.com/users/${userId}`;

  try {
    const response = await fetch(url);

    // 1) Controllo dello stato HTTP
    // fetch non fallisce automaticamente per errori 404, 500, ecc.
    if (!response.ok) {
      throw new Error(`Errore HTTP: ${response.status} ${response.statusText}`);
    }

    // 2) Verifica del Content-Type
    const contentType = response.headers.get("content-type");
    if (!contentType || !contentType.includes("application/json")) {
      throw new Error("La risposta non è in formato JSON.");
    }

    // 3) Lettura del body JSON
    const data = await response.json();

    // 4) Validazione minima della struttura
    if (!data || typeof data !== "object") {
      throw new Error("Formato dati non valido: il JSON non è un oggetto.");
    }

    const requiredFields = ["id", "name", "email", "address"];
    for (const field of requiredFields) {
      if (!(field in data)) {
        throw new Error(`Campo mancante nella risposta: ${field}`);
      }
    }

    // 5) Validazione di alcuni campi annidati
    if (
      !data.address ||
      typeof data.address !== "object" ||
      !data.address.city ||
      !data.address.zipcode
    ) {
      throw new Error("Indirizzo non valido o incompleto.");
    }

    // 6) Normalizzazione dei dati per l´applicazione
    const userProfile = {
      id: data.id,
      name: data.name,
      email: data.email,
      city: data.address.city,
      zipcode: data.address.zipcode,
      company: data.company?.name ?? "Azienda non disponibile"
    };

    return userProfile;
  } catch (error) {
    // Gestione centralizzata degli errori
    console.error("Impossibile caricare il profilo utente:", error.message);
    return null;
  }
}

// Esempio di utilizzo
(async () => {
  const user = await getUserProfile(1);

  if (user) {
    console.log("Profilo utente caricato con successo:");
    console.log(user);
  } else {
    console.log("Nessun profilo disponibile.");
  }
})();

Spiegazione

Vediamo il codice passo per passo, perché il valore di questo approccio sta proprio nella sequenza delle verifiche.

1. Chiamata fetch

La funzione fetch(url) invia una richiesta HTTP e restituisce una Promise. Con await otteniamo l’oggetto risposta, che contiene intestazioni, stato HTTP e metodi per leggere il body.

2. Controllo di response.ok

Uno degli errori più comuni è pensare che fetch lanci un’eccezione quando il server risponde con 404 o 500. In realtà non succede: la Promise si risolve comunque, ma response.ok sarà false. Per questo è fondamentale verificare lo stato HTTP prima di leggere il contenuto.

3. Controllo del tipo di contenuto

Non tutte le risposte che arrivano da un endpoint sono necessariamente JSON. Un server potrebbe restituire HTML, testo semplice o un errore personalizzato. Controllare l’header content-type aiuta a evitare errori quando si chiama response.json() su un body non compatibile.

4. Lettura del JSON

Il metodo response.json() converte il body della risposta in un oggetto JavaScript. Questa operazione può fallire se il contenuto non è JSON valido. Per questo è utile racchiuderla in un blocco try...catch.

5. Validazione della struttura

Una volta ottenuto il dato, non bisogna fidarsi ciecamente della sua forma. Nel nostro esempio controlliamo che:

  • il dato sia un oggetto;
  • alcuni campi fondamentali siano presenti;
  • i campi annidati, come address, abbiano una struttura sensata.

Questa validazione è importante perché molte API possono cambiare nel tempo, oppure restituire risposte parziali in caso di dati incompleti.

6. Normalizzazione dei dati

Spesso l’API restituisce più informazioni di quelle che servono davvero all’applicazione. Conviene quindi creare un oggetto “normalizzato”, più semplice e coerente con il resto del codice. Nel nostro esempio estraiamo solo i campi utili: nome, email, città, CAP e nome azienda.

7. Gestione degli errori

Il blocco catch intercetta sia errori di rete sia errori generati manualmente con throw new Error(). Questo approccio rende la funzione più affidabile e più facile da integrare in una UI, in un servizio o in un componente frontend.

Best practice

Quando lavori con risposte JSON tramite Fetch API, queste pratiche ti aiutano a scrivere codice più solido e manutenibile:

  • Controlla sempre response.ok prima di leggere il body.
  • Verifica il content-type per evitare di interpretare come JSON risposte che non lo sono.
  • Valida la struttura dei dati ricevuti, soprattutto per campi obbligatori o annidati.
  • Normalizza i dati appena li ricevi, così il resto dell’app lavora con un formato stabile.
  • Usa try...catch per centralizzare la gestione degli errori.
  • Non assumere mai che l’API sia perfetta: anche un endpoint ben fatto può restituire dati incompleti o inattesi.
  • Se possibile, definisci uno schema dei dati attesi, soprattutto in progetti medio-grandi.

Un errore frequente è usare direttamente i dati ricevuti nella UI senza controlli. Per esempio, se data.address.city non esiste, la tua applicazione può andare in errore. Meglio intercettare il problema subito e mostrare un messaggio chiaro all’utente o un fallback sensato.

Un’altra buona pratica è separare la logica di rete dalla logica di presentazione. La funzione che chiama l’API dovrebbe occuparsi di recuperare e validare i dati, mentre il componente o la pagina dovrebbe limitarsi a mostrarli.

Riepilogo

La Fetch API è uno strumento semplice ma molto potente. Quando però si lavora con risposte JSON, il vero salto di qualità sta nella validazione dei dati ricevuti. Non basta fare la richiesta: bisogna verificare lo stato HTTP, controllare il tipo di contenuto, leggere il JSON in sicurezza e assicurarsi che la struttura sia quella attesa.

In sintesi:

  • fetch() esegue la richiesta ma non segnala da solo gli errori HTTP.
  • response.ok serve a distinguere risposte valide da risposte fallite.
  • response.json() legge il body, ma richiede contenuto JSON corretto.
  • La validazione dei campi evita bug e comportamenti imprevedibili.
  • Una buona normalizzazione rende il codice più pulito e riutilizzabile.

Se impari a gestire bene le risposte JSON, renderai le tue chiamate HTTP molto più affidabili e il tuo frontend molto più robusto.

Approfondisci con risorse ufficiali

  • MDN Web Docs - Fetch API: documentazione completa e aggiornata sulla Fetch API.
  • MDN Web Docs - Response: dettagli sull’oggetto risposta e sulle sue proprietà.
  • MDN Web Docs - Using Fetch: guida pratica all’uso di fetch con esempi.
  • WHATWG Fetch Standard: specifica ufficiale del comportamento della Fetch API.
  • MDN Web Docs - JSON: riferimento utile per lavorare con dati JSON in JavaScript.

SHARE