Fetch API et requêtes HTTP

Fetch API est la nouvelle façon de faire des requêtes HTTP en JavaScript
Elle remplace XMLHttpRequest (XHR) et utilise les Promises (XMLHttpRequest était l'ancienne version de faire, on
la verra pas ici, étant donné que c'est une méthode 'outdated' ;) )
On peut également utiliser AXIOS ?? -> historiquement, c'était une meilleure façon de faire que XMLHttpRequest,
mais depuis la sortie de fetch, en natif en js ce n'est plus si vrai, et dans la plupart des cas fetch est plus léger, natif.
et tout autant efficace.

Introduction à Fetch

Syntaxe de base: fetch(url, options)
fetch() retourne toujours une Promise

Requête GET simple

async function exempleGetSimple() {
    try {
        // Requête vers une API de test (JSONPlaceholder)
        const response = await fetch('https://jsonplaceholder.typicode.com/users/1');

        // Vérifier si la requête a réussi
        if (!response.ok) {
            throw new Error(`Erreur HTTP: ${response.status}`);
        }

        // Convertir la réponse en JSON
        const utilisateur = await response.json();
        console.log("Utilisateur récupéré:", utilisateur);

        return utilisateur;
    } catch (erreur) {
        console.error("Erreur lors de la requête:", erreur.message);
        throw erreur;
    }
}

// Appel de la fonction (sera exécuté seulement si l'API est accessible)
// exempleGetSimple();

Méthodes HTTP

Les principales méthodes HTTP et leur usage

GET : Récupérer des données

async function obtenirUtilisateurs() {
    try {
        const response = await fetch('https://jsonplaceholder.typicode.com/users');

        if (!response.ok) {
            throw new Error(`Erreur: ${response.status} ${response.statusText}`);
        }

        const utilisateurs = await response.json();
        console.log(`${utilisateurs.length} utilisateurs récupérés`);

        return utilisateurs;
    } catch (erreur) {
        console.error("Erreur GET:", erreur.message);
        return [];
    }
}

POST : Créer des données

async function creerUtilisateur(nouvelUtilisateur) {
    try {
        const response = await fetch('https://jsonplaceholder.typicode.com/users', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(nouvelUtilisateur)
        });

        if (!response.ok) {
            throw new Error(`Erreur: ${response.status}`);
        }

        const utilisateurCree = await response.json();
        console.log("Utilisateur créé:", utilisateurCree);

        return utilisateurCree;
    } catch (erreur) {
        console.error("Erreur POST:", erreur.message);
        throw erreur;
    }
}

// Exemple d'utilisation
const nouvelUtilisateur = {
    name: "Jean Dupont",
    email: "jean.dupont@example.com",
    phone: "01 23 45 67 89"
};

// creerUtilisateur(nouvelUtilisateur);

PUT : Mettre à jour complètement

async function mettreAJourUtilisateur(id, utilisateur) {
    try {
        const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`, {
            method: 'PUT',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(utilisateur)
        });

        const utilisateurMisAJour = await response.json();
        console.log("Utilisateur mis à jour:", utilisateurMisAJour);

        return utilisateurMisAJour;
    } catch (erreur) {
        console.error("Erreur PUT:", erreur.message);
        throw erreur;
    }
}

PATCH : Mettre à jour partiellement

async function mettreAJourPartiel(id, modifications) {
    try {
        const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`, {
            method: 'PATCH',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(modifications)
        });

        const utilisateurModifie = await response.json();
        console.log("Utilisateur modifié:", utilisateurModifie);

        return utilisateurModifie;
    } catch (erreur) {
        console.error("Erreur PATCH:", erreur.message);
        throw erreur;
    }
}

DELETE : Supprimer des données

async function supprimerUtilisateur(id) {
    try {
        const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`, {
            method: 'DELETE'
        });

        if (response.ok) {
            console.log(`Utilisateur ${id} supprimé`);
            return true;
        } else {
            throw new Error(`Impossible de supprimer l'utilisateur ${id}`);
        }
    } catch (erreur) {
        console.error("Erreur DELETE:", erreur.message);
        return false;
    }
}

Gestion des Headers

Les headers permettent d'envoyer des métadonnées avec les requêtes

Headers d'authentification

async function requeteAvecAuth(token) {
    try {
        const response = await fetch('https://api.example.com/protected', {
            headers: {
                'Authorization': `Bearer ${token}`,
                'Content-Type': 'application/json',
                'Accept': 'application/json'
            }
        });

        return await response.json();
    } catch (erreur) {
        console.error("Erreur authentification:", erreur.message);
        throw erreur;
    }
}

Headers personnalisés

async function requeteAvecHeadersCustom() {
    const headers = new Headers();
    headers.append('Content-Type', 'application/json');
    headers.append('X-API-Key', 'ma-clé-secrète');
    headers.append('User-Agent', 'MonApp/1.0');

    try {
        const response = await fetch('https://api.example.com/data', {
            method: 'GET',
            headers: headers
        });

        return await response.json();
    } catch (erreur) {
        console.error("Erreur headers custom:", erreur.message);
        throw erreur;
    }
}

Vérification du statut de réponse

async function verifierStatutReponse(url) {
    try {
        const response = await fetch(url);

        console.log("Statut:", response.status);
        console.log("Statut texte:", response.statusText);
        console.log("URL finale:", response.url);
        console.log("Redirected:", response.redirected);

        // Différents codes de statut
        if (response.status >= 200 && response.status < 300) {
            console.log("Succès");
        } else if (response.status >= 300 && response.status < 400) {
            console.log("Redirection");
        } else if (response.status >= 400 && response.status < 500) {
            console.log("Erreur client");
        } else if (response.status >= 500) {
            console.log("Erreur serveur");
        }

        return response;
    } catch (erreur) {
        console.error("Erreur réseau:", erreur.message);
        throw erreur;
    }
}

Timeout et annulation

Timeout avec AbortController

async function requeteAvecTimeout(url, timeoutMs = 5000) {
    // Créer un AbortController pour annuler la requête
    const controller = new AbortController();

    // Programmer l'annulation après le timeout
    const timeoutId = setTimeout(() => {
        controller.abort();
    }, timeoutMs);

    try {
        const response = await fetch(url, {
            signal: controller.signal
        });

        // Annuler le timeout si la requête réussit
        clearTimeout(timeoutId);

        return await response.json();
    } catch (erreur) {
        clearTimeout(timeoutId);

        if (erreur.name === 'AbortError') {
            throw new Error(`Timeout après ${timeoutMs}ms`);
        }
        throw erreur;
    }
}

Annulation manuelle

let controllerGlobal = null;
function demarrerRequeteLongue() {
    controllerGlobal = new AbortController();
    return fetch('https://httpbin.org/delay/10', {
        signal: controllerGlobal.signal
    })
    .then(response => response.json())
    .then(data => console.log("Requête terminée:", data))
    .catch(erreur => {
        if (erreur.name === 'AbortError') {
            console.log("Requête annulée par l'utilisateur");
        } else {
            console.error("Erreur:", erreur.message);
        }
    });
}

function annulerRequete() {
    if (controllerGlobal) {
        controllerGlobal.abort();
        console.log("Annulation demandée");
    }
}