Menu

Fetch API in JavaScript: richieste, JSON ed errori

Guida pratica alla Fetch API in JavaScript: richieste GET e POST, parsing del JSON, gestione corretta degli errori e annullamento delle richieste lente.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Fetch è un client HTTP basato sulle promise

fetch è integrato nei browser e nelle versioni moderne di Node. Gli passi un URL e ti restituisce una Promise che si risolve in un oggetto Response. Nella sua essenza, l'API è tutta qui:

Ci sono due .then perché i passaggi asincroni sono due: prima arrivano gli header della risposta (è con questi che si risolve la prima promise), poi il body viene letto e analizzato (response.json() è a sua volta una promise). Il body non viene scaricato finché non lo chiedi.

Lo stesso flusso con async/await si legge come normale codice dall'alto in basso:

Due await, due punti di sospensione. Lo stesso lavoro, con un ordine di lettura più chiaro.

L'oggetto Response

Quello che ricevi non è il body: è un oggetto Response con dei metadati e dei metodi per leggere il body in formati diversi:

Puoi leggere il body con .json(), .text(), .blob(), .arrayBuffer() o .formData(). Ognuno restituisce una promise. Il body si può leggere una volta sola: se chiami .json() due volte sulla stessa risposta, la seconda chiamata lancia un errore.

Il grande tranello: gli errori HTTP non rifiutano la promise

Questo inganna quasi tutti all'inizio con fetch. Una risposta 404 o 500 non è un rifiuto. La promise si risolve normalmente, con response.ok === false. Fetch rifiuta solo quando la richiesta stessa non è riuscita a completarsi: errore DNS, niente rete, blocco CORS.

Quindi un fetch ingenuo ti passa senza problemi una pagina di errore e poi va in crash più avanti su .json():

La soluzione è controllare tu response.ok e lanciare un errore se il server ha restituito uno status di errore:

Abituati a scrivere quel blocco if (!response.ok). Va messo in ogni wrapper di fetch che scrivi.

Inviare una richiesta POST

GET è il metodo predefinito. Per qualsiasi altro, passa un secondo argomento, un oggetto di opzioni:

Tre cose da notare:

  • method vale "GET" per impostazione predefinita. Impostalo esplicitamente per POST, PUT, DELETE e PATCH.
  • body accetta una stringa (oppure FormData, Blob, ecc.): fetch non serializza gli oggetti per te. Il JSON.stringify(...) tocca a te.
  • L'header Content-Type dice al server come interpretare il body. Se lo dimentichi, la maggior parte dei server tratterà il body come testo semplice.

Header, query string e altre opzioni

Gli header sono semplicemente un oggetto (o un'istanza di Headers). Le query string le costruisci tu, di solito con URLSearchParams:

URLSearchParams si occupa della codifica al posto tuo (spazi, e commerciali, unicode), così non ti ritrovi con URL rotti quando l'input contiene caratteri da fare l'escape.

Altre opzioni che vedrai nel codice reale: credentials: "include" per inviare i cookie tra origini diverse, cache: "no-store" per saltare la cache HTTP, mode: "cors" (di solito il valore predefinito) per controllare il comportamento CORS.

Annullare una richiesta con AbortController

A volte vuoi rinunciare: l'utente ha digitato una nuova ricerca, oppure la richiesta ci mette troppo. Il meccanismo è AbortController:

controller.abort() fa rifiutare la promise di fetch con una DOMException il cui name è "AbortError". Il blocco finally cancella il timeout, così una richiesta andata a buon fine non lascia in giro un timer pendente.

Questo schema (fetch più timeout più pulizia) merita di essere racchiuso in un helper da riutilizzare ovunque.

Un wrapper riutilizzabile

Mettendo tutto insieme ottieni un piccolo helper che gestisce il codice ripetitivo una volta per tutte:

Un solo punto per cambiare gli header, uno per gestire gli errori, uno per occuparsi delle risposte vuote. Ogni applicazione non banale finisce per avere qualcosa del genere.

Prossimo argomento: gestione degli errori nel codice asincrono

Fetch è uno dei posti in cui gli errori asincroni saltano fuori più spesso, e il controllo di response.ok è solo un pezzo del puzzle. La prossima pagina parla della gestione degli errori con le promise e async/await: dove finiscono gli errori, come catturarli e le trappole che li lasciano passare in silenzio.

Domande frequenti

Come si usa fetch in JavaScript?

Chiama fetch(url) con l'URL che ti interessa. Restituisce una Promise che si risolve in un oggetto Response. Per fare il parsing del body chiami response.json() (anche questa è una promise). Con async/await: const res = await fetch(url); const data = await res.json();.

Come faccio una richiesta POST con fetch?

Passa un secondo argomento con method: 'POST', un oggetto headers (di solito 'Content-Type': 'application/json') e un body. Gli oggetti vanno convertiti in stringa con JSON.stringify(...): fetch non serializza il body al posto tuo.

Perché fetch non rifiuta la promise con un 404 o un 500?

Fetch rifiuta solo in caso di errori di rete: errori DNS, assenza di connessione, blocchi CORS. Per la promise, gli status HTTP di errore sono comunque risposte riuscite. Devi controllare tu response.ok (true tra 200 e 299) o response.status e lanciare un errore se il server ha risposto con un errore.

Posso annullare una richiesta fetch?

Sì, con AbortController. Ne crei uno, passi il suo signal a fetch nell'oggetto delle opzioni e chiami controller.abort() quando vuoi annullare. La promise di fetch viene rifiutata con un AbortError che puoi gestire nel catch.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA