Introduction à l'API Fetch
L'API Fetch est l'interface moderne de JavaScript pour effectuer des requêtes HTTP depuis le navigateur. Elle remplace avantageusement l'ancien XMLHttpRequest avec une syntaxe plus propre basée sur les Promises, compatible avec async/await.
Requête GET basique
// Syntaxe de base
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Erreur:', error));
// Avec async/await (recommandé)
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
console.log(data);
return data;
} catch (error) {
console.error('Erreur de fetch:', error);
}
}
Les méthodes HTTP avec Fetch
GET : récupérer des données
// GET avec paramètres d'URL
const params = new URLSearchParams({
page: 1,
limit: 10,
search: 'javascript'
});
const response = await fetch(`https://api.example.com/posts?${params}`);
const posts = await response.json();
POST : envoyer des données
// POST avec JSON
const response = await fetch('https://api.example.com/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_TOKEN'
},
body: JSON.stringify({
title: 'Mon article',
content: 'Contenu de l\'article',
author: 'Tourak Digital'
})
});
const newPost = await response.json();
// POST avec FormData (pour les fichiers)
const formData = new FormData();
formData.append('name', 'John');
formData.append('avatar', fileInput.files[0]);
const response = await fetch('https://api.example.com/upload', {
method: 'POST',
body: formData // Pas besoin de Content-Type, le navigateur le gère
});
PUT et PATCH : modifier des données
// PUT : remplacement complet
await fetch('https://api.example.com/posts/42', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
title: 'Titre modifié',
content: 'Contenu mis à jour'
})
});
// PATCH : modification partielle
await fetch('https://api.example.com/posts/42', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'Nouveau titre uniquement' })
});
DELETE : supprimer des données
const response = await fetch('https://api.example.com/posts/42', {
method: 'DELETE',
headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
});
if (response.ok) {
console.log('Post supprimé avec succès');
}
L'objet Response en détail
const response = await fetch(url);
// Propriétés
response.ok; // true si status 200-299
response.status; // Code HTTP (200, 404, 500...)
response.statusText; // Message ('OK', 'Not Found'...)
response.url; // URL finale (après redirections)
response.redirected; // true si redirigé
response.headers; // Headers de réponse
response.type; // 'basic', 'cors', 'opaque'
// Méthodes pour lire le corps (ne peut être lu qu'une seule fois !)
const json = await response.json(); // Parse en JSON
const text = await response.text(); // Texte brut
const blob = await response.blob(); // Données binaires
const buffer = await response.arrayBuffer(); // ArrayBuffer
const form = await response.formData(); // FormData
// Lire les headers
response.headers.get('Content-Type');
response.headers.get('X-Total-Count');
response.headers.has('Authorization');
Gestion robuste des erreurs
// ATTENTION : fetch ne rejette PAS la promesse pour les erreurs HTTP (404, 500)
// Il rejette uniquement pour les erreurs réseau
async function safeFetch(url, options = {}) {
try {
const response = await fetch(url, options);
// Vérifier le code HTTP
if (!response.ok) {
const errorBody = await response.text();
throw new Error(
`HTTP ${response.status}: ${response.statusText}\n${errorBody}`
);
}
// Vérifier le Content-Type
const contentType = response.headers.get('Content-Type');
if (contentType && contentType.includes('application/json')) {
return await response.json();
}
return await response.text();
} catch (error) {
if (error.name === 'AbortError') {
console.log('Requête annulée');
} else if (error.name === 'TypeError') {
console.error('Erreur réseau ou CORS:', error.message);
} else {
console.error('Erreur:', error.message);
}
throw error;
}
}
Fonctionnalités avancées
Annuler une requête avec AbortController
const controller = new AbortController();
// Timeout automatique
const timeoutId = setTimeout(() => controller.abort(), 5000);
try {
const response = await fetch(url, {
signal: controller.signal
});
clearTimeout(timeoutId);
const data = await response.json();
} catch (error) {
if (error.name === 'AbortError') {
console.log('Requête annulée (timeout 5s)');
}
}
// Annulation manuelle (ex: changement de page)
function cleanup() {
controller.abort();
}
Requêtes parallèles
// Promise.all : toutes doivent réussir
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())
]);
// Promise.allSettled : récupère tout, même les erreurs
const results = await Promise.allSettled([
fetch('/api/users').then(r => r.json()),
fetch('/api/posts').then(r => r.json()),
fetch('/api/broken').then(r => r.json())
]);
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log('Succès:', result.value);
} else {
console.log('Échec:', result.reason);
}
});
Intercepteur de requêtes (fetch wrapper)
// Créer un client API réutilisable
class ApiClient {
constructor(baseUrl, defaultHeaders = {}) {
this.baseUrl = baseUrl;
this.defaultHeaders = defaultHeaders;
}
async request(endpoint, options = {}) {
const url = `${this.baseUrl}${endpoint}`;
const config = {
...options,
headers: {
'Content-Type': 'application/json',
...this.defaultHeaders,
...options.headers
}
};
if (config.body && typeof config.body === 'object') {
config.body = JSON.stringify(config.body);
}
const response = await fetch(url, config);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
get(endpoint) { return this.request(endpoint); }
post(endpoint, body) { return this.request(endpoint, { method: 'POST', body }); }
put(endpoint, body) { return this.request(endpoint, { method: 'PUT', body }); }
delete(endpoint) { return this.request(endpoint, { method: 'DELETE' }); }
}
// Utilisation
const api = new ApiClient('https://api.example.com', {
'Authorization': 'Bearer TOKEN'
});
const posts = await api.get('/posts');
const newPost = await api.post('/posts', { title: 'Nouveau' });
Fetch vs Axios vs XMLHttpRequest
| Critère | Fetch | Axios | XMLHttpRequest |
|---|---|---|---|
| Natif | Oui | Non (librairie) | Oui |
| Promises | Oui | Oui | Non (callbacks) |
| Annulation | AbortController | CancelToken / AbortController | abort() |
| Intercepteurs | Manuel | Intégrés | Manuel |
| Gestion erreurs HTTP | Manuelle (response.ok) | Automatique | Manuelle |
| Progress upload | Non natif | Oui | Oui |
| Taille | 0 Ko | ~13 Ko | 0 Ko |
| Recommandé | Projets légers | Apps complexes | Legacy |
Conclusion
L'API Fetch est l'outil natif et moderne pour les requêtes HTTP en JavaScript. Avec async/await, AbortController et une bonne gestion des erreurs, elle couvre la majorité des besoins. Pour les projets plus complexes nécessitant des intercepteurs ou le suivi de progression, Axios reste un excellent complément.
Chez Tourak Digital, nous développons des applications web performantes avec les meilleures pratiques JavaScript. Contactez-nous.