Stai ancora importando Axios o jQuery solo per fare una richiesta HTTP? Se il tuo progetto ha bisogno di due librerie prima ancora di scrivere una riga di logica, qualcosa non quadra. La Fetch API è nativa, moderna e copre il 95% dei casi d'uso reali. Noi di Meteora Web la usiamo ogni giorno per costruire applicazioni che devono comunicare con API REST, senza appesantire il bundle. In questa guida vediamo come sfruttarla al massimo, con esempi pronti da copiare e best practice che nascono dall'esperienza sul campo.
Come funziona la Fetch API rispetto a XMLHttpRequest?
La Fetch API è stata introdotta per sostituire la vecchia XMLHttpRequest (XHR) con un'interfaccia più pulita, basata su Promise. Mentre XHR richiedeva callback annidati e gestione manuale degli stati, Fetch restituisce una Promise che si risolve con un oggetto Response. Questo permette di concatenare operazioni con .then() o async/await in modo lineare.
Esempio base:
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Errore:', error));
Con async/await diventa ancora più leggibile:
async function getData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Errore:', error);
}
}
Rispetto a XHR, Fetch è più conciso, integrata nativamente (nessuna dipendenza) e supporta nativamente CORS, credenziali e stream. L'unico svantaggio storico è la mancanza di un timeout nativo e la gestione degli errori HTTP (Fetch non rigetta la Promise per status 4xx o 5xx). Vediamo come risolvere.
Sponsored Protocol
Riferimento ufficiale: MDN Fetch API
Come gestire errori e timeout con la Fetch API?
Un errore comune: se un server risponde con 404 o 500, Fetch non lancia un'eccezione. La Promise si risolve comunque, e devi controllare manualmente il codice di stato. Ecco come farlo correttamente:
async function safeFetch(url, options = {}) {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}
Timeout: Fetch non ha un timeout nativo. Si aggira con AbortController. Un pattern che usiamo nei nostri progetti:
async function fetchWithTimeout(url, timeout = 5000) {
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, { signal: controller.signal });
clearTimeout(id);
if (!response.ok) throw new Error('Errore HTTP');
return await response.json();
} catch (error) {
clearTimeout(id);
if (error.name === 'AbortError') {
throw new Error('Richiesta scaduta');
}
throw error;
}
}
Questo garantisce che le richieste non rimangano in sospeso per minuti, bloccando l'UI.
Sponsored Protocol
Quali sono le best practice per il fetching di dati in produzione?
Quando costruisci un'applicazione reale, non ti basta fare una fetch ogni volta che serve un dato. Ecco alcune regole che applichiamo quotidianamente:
1. Centralizza le chiamate API
Non sparpagliare fetch in ogni componente. Crea un modulo services/api.js con funzioni riutilizzabili:
export async function getUsers() {
const res = await fetch('/api/users');
if (!res.ok) throw new Error('Errore nel caricamento utenti');
return res.json();
}
export async function createUser(data) {
const res = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (!res.ok) throw new Error('Errore creazione utente');
return res.json();
}
2. Usa un interceptor per auth e logging
Invece di passare token manualmente, crea un wrapper fetch personalizzato:
Sponsored Protocol
const apiFetch = async (url, options = {}) => {
const token = localStorage.getItem('token');
const headers = {
'Content-Type': 'application/json',
...options.headers,
...(token && { 'Authorization': `Bearer ${token}` })
};
try {
const response = await fetch(url, { ...options, headers });
if (!response.ok) {
if (response.status === 401) {
// redirect al login
window.location.href = '/login';
}
throw new Error(`Errore API: ${response.status}`);
}
return response.json();
} catch (error) {
console.error('API call failed:', url, error);
throw error;
}
};
3. Gestisci il caricamento e lo stato vuoto
Ogni richiesta asincrona dovrebbe avere tre stati: loading, success, error. Un pattern semplice:
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
fetch(url)
.then(res => { if (!res.ok) throw new Error(); return res.json(); })
.then(data => { if (!cancelled) { setData(data); setLoading(false); } })
.catch(err => { if (!cancelled) { setError(err); setLoading(false); } });
return () => { cancelled = true; };
}, [url]);
return { data, loading, error };
}
Se usi React, questo hook evita memory leak su componenti smontati.
Sponsored Protocol
Come gestire richieste concorrenti e cancellazione?
Spesso devi fare più chiamate parallele (es. dashboard con widget indipendenti) o annullare una richiesta in corso quando l'utente cambia pagina. Fetch + Promise.all risolve il primo scenario:
const [users, posts, comments] = await Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/posts').then(r => r.json()),
fetch('/api/comments').then(r => r.json())
]);
Per la cancellazione, usiamo AbortController come visto sopra, ma in un contesto di componenti che si montano/smontano. Un pattern robusto:
function createCancellableFetch() {
let controller = null;
async function fetchCancellable(url, options) {
if (controller) controller.abort();
controller = new AbortController();
const response = await fetch(url, { ...options, signal: controller.signal });
return response.json();
}
function cancelAll() {
if (controller) controller.abort();
}
return { fetchCancellable, cancelAll };
}
Questo è utile per autocomplete o ricerche in tempo reale.
Sponsored Protocol
In sintesi: cosa fare adesso
- Sostituisci Axios con Fetch nei nuovi progetti. Per progetti esistenti, valuta il refactoring se il bundle è un problema.
- Implementa il pattern di controllo errori e timeout con AbortController. Copia i due snippet qui sopra.
- Crea un modulo services centralizzato per tutte le chiamate API del tuo progetto.
- Aggiungi un wrapper per autenticazione che inietti il token automaticamente.
- Testa la cancellazione delle richieste in componenti che si smontano (es. durante navigazione).
La Fetch API non è solo un'alternativa più moderna: è lo strumento giusto quando vuoi mantenere il controllo sulle dipendenze e scrivere codice che capisci fino all'ultima riga. Per approfondire altre feature di JavaScript moderno, dai un'occhiata al nostro pillar su JavaScript ES2024+.