Développement Web

Comment utiliser les API REST avec JavaScript (fetch)

14 دقائق للقراءة

Prérequis

  • Niveau : bases JavaScript et asynchrone (cf. les bases JS).
  • Outils : VS Code, navigateur moderne (DevTools onglet Network pour observer les requêtes).
  • Optionnel : compte gratuit sur OpenWeatherMap pour l’exemple météo (clé API requise).
  • Temps estimé : 1 h.

Pourquoi maîtriser fetch ?

fetch() est l’API native du navigateur pour communiquer avec le monde extérieur : récupérer des données, envoyer un formulaire, charger des médias, intégrer un service tiers (Stripe, Mapbox, OpenAI…). Sans elle, vos pages restent statiques. Avec elle, elles deviennent des applications.

Les API REST : comment les sites échangent des données

Une API REST permet à votre site de communiquer avec des services externes : récupérer la météo, afficher des actualités, envoyer des données à un serveur. L’outil JavaScript pour cela s’appelle fetch().

Comprendre une requête API

Analogie : Une API, c’est comme un serveur au restaurant. Vous (le client/navigateur) passez une commande (requête), le serveur transmet à la cuisine (le serveur distant), et revient avec votre plat (la réponse).

Méthode Action Exemple
GET Lire des données Récupérer la liste des articles
POST Créer des données Ajouter un nouvel article
PUT Modifier des données Mettre à jour un article
DELETE Supprimer des données Supprimer un article

Requête GET simple avec fetch()

Voici la mise en pratique pour Requête GET simple avec fetch(). Le bloc ci-dessous est copiable directement dans votre projet, lisible ligne par ligne. Lisez-le une première fois en survol pour repérer la structure générale, puis adaptez les noms de variables, identifiants et valeurs à votre contexte avant de l’exécuter en local.

// Récupérer des données depuis une API publique
fetch('https://jsonplaceholder.typicode.com/posts')
    .then(response => response.json())  // Convertir en JSON
    .then(data => {
        console.log(data);  // Tableau d'articles
        console.log(data[0].title);  // Titre du premier article
    })
    .catch(error => {
        console.error('Erreur :', error);
    });

Avec async/await (syntaxe moderne recommandée)

Voici la mise en pratique pour Avec async/await (syntaxe moderne recommandée). Le bloc ci-dessous est copiable directement dans votre projet, lisible ligne par ligne. Lisez-le une première fois en survol pour repérer la structure générale, puis adaptez les noms de variables, identifiants et valeurs à votre contexte avant de l’exécuter en local.

async function chargerArticles() {
    try {
        const response = await fetch('https://jsonplaceholder.typicode.com/posts');
        
        // Vérifier que la requête a réussi
        if (!response.ok) {
            throw new Error('Erreur HTTP : ' + response.status);
        }
        
        const articles = await response.json();
        
        // Afficher les articles dans la page
        const conteneur = document.getElementById('articles');
        articles.slice(0, 10).forEach(article => {
            conteneur.innerHTML += `
                <div class="article">
                    <h3>${article.title}</h3>
                    <p>${article.body}</p>
                </div>
            `;
        });
        
    } catch (erreur) {
        console.error('Impossible de charger les articles :', erreur);
        document.getElementById('articles').innerHTML = 
            '<p>Erreur de chargement. Vérifiez votre connexion.</p>';
    }
}

chargerArticles();

Requête POST : envoyer des données

Voici la mise en pratique pour Requête POST : envoyer des données. Le bloc ci-dessous est copiable directement dans votre projet, lisible ligne par ligne. Lisez-le une première fois en survol pour repérer la structure générale, puis adaptez les noms de variables, identifiants et valeurs à votre contexte avant de l’exécuter en local.

async function creerArticle(titre, contenu) {
    try {
        const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                title: titre,
                body: contenu,
                userId: 1
            })
        });
        
        const résultat = await response.json();
        console.log('Article créé :', résultat);
        return résultat;
        
    } catch (erreur) {
        console.error('Erreur lors de la création :', erreur);
    }
}

// Utilisation
creerArticle('Mon article', 'Le contenu de mon article');

Exemple concret : App Météo

Voici la mise en pratique pour Exemple concret : App Météo. Le bloc ci-dessous est copiable directement dans votre projet, lisible ligne par ligne. Lisez-le une première fois en survol pour repérer la structure générale, puis adaptez les noms de variables, identifiants et valeurs à votre contexte avant de l’exécuter en local.

<div id="meteo">
    <input type="text" id="ville" placeholder="Entrez une ville (ex: Dakar)">
    <button onclick="chercherMeteo()">Rechercher</button>
    <div id="resultat-meteo"></div>
</div>

<script>
async function chercherMeteo() {
    const ville = document.getElementById('ville').value;
    const clé = 'VOTRE_CLE_API'; // Inscrivez-vous sur openweathermap.org
    const url = `https://api.openweathermap.org/data/2.5/weather?q=${ville}&appid=${clé}&units=metric&lang=fr`;
    
    try {
        const response = await fetch(url);
        if (!response.ok) throw new Error('Ville non trouvée');
        
        const data = await response.json();
        
        document.getElementById('resultat-meteo').innerHTML = `
            <div class="meteo-card">
                <h2>${data.name}, ${data.sys.country}</h2>
                <p class="temp">${Math.round(data.main.temp)}°C</p>
                <p>${data.weather[0].description}</p>
                <p>Humidité : ${data.main.humidity}%</p>
                <p>Vent : ${(data.wind.speed * 3.6).toFixed(1)} km/h</p>
            </div>
        `;
    } catch (erreur) {
        document.getElementById('resultat-meteo').innerHTML = 
            '<p style="color:red;">❌ ' + erreur.message + '</p>';
    }
}
</script>

Gérer le chargement et les erreurs proprement

Voici la mise en pratique pour Gérer le chargement et les erreurs proprement. Le bloc ci-dessous est copiable directement dans votre projet, lisible ligne par ligne. Lisez-le une première fois en survol pour repérer la structure générale, puis adaptez les noms de variables, identifiants et valeurs à votre contexte avant de l’exécuter en local.

async function chargerDonnees(url) {
    const conteneur = document.getElementById('contenu');
    
    // 1. Afficher un loader
    conteneur.innerHTML = '<div class="loader">Chargement...</div>';
    
    try {
        const response = await fetch(url);
        
        if (!response.ok) {
            throw new Error(`Erreur ${response.status}: ${response.statusText}`);
        }
        
        const data = await response.json();
        
        // 2. Afficher les données
        conteneur.innerHTML = data.map(item => `
            <div class="item">
                <h3>${item.title}</h3>
                <p>${item.body}</p>
            </div>
        `).join('');
        
    } catch (erreur) {
        // 3. Afficher l'erreur
        conteneur.innerHTML = `
            <div class="erreur">
                <p>⚠️ Impossible de charger les données</p>
                <p>${erreur.message}</p>
                <button onclick="chargerDonnees('${url}')">Réessayer</button>
            </div>
        `;
    }
}

API publiques gratuites pour s’entraîner

API Données URL
JSONPlaceholder Faux articles, users jsonplaceholder.typicode.com
OpenWeatherMap Météo mondiale openweathermap.org/api
REST Countries Infos pays restcountries.com
PokeAPI Pokémon pokeapi.co

Erreurs fréquentes

« CORS error » dans la console

Cause : l’API distante n’autorise pas votre origine (en-tête Access-Control-Allow-Origin manquant).
Solution : consultez la doc de l’API. Pour vos APIs internes, ajoutez les en-têtes CORS côté serveur. Pour contourner en dev, utilisez un proxy (ex. Vite proxy).

fetch ne lance pas d’erreur sur un 404 / 500

Cause : seules les erreurs réseau déclenchent catch. Un statut HTTP 4xx/5xx reste une « réponse réussie ».
Solution : testez toujours response.ok et throw manuellement si nécessaire (comme dans cet article).

Clé API exposée dans le code front

Cause : on hardcode la clé dans le JS livré au navigateur — elle est visible par tous.
Solution : pour les APIs sensibles, faites passer la requête par un proxy sur votre backend qui ajoute la clé. N’exposez en front que les clés vraiment publiques (Mapbox public token, etc.).

Pas de gestion du loading ni du retry

Cause : on lance la requête et on attend.
Solution : affichez systématiquement un état « chargement » avant await, et proposez un bouton « Réessayer » dans le catch (cf. exemple de l’article).

Exercice pratique

🎯 Défi : Annuaire des pays africains

  1. Utilisez l’API REST Countries pour récupérer les pays d’Afrique
  2. Affichez pour chaque pays : drapeau, nom, capitale, population
  3. Ajoutez un champ de recherche pour filtrer par nom
  4. Ajoutez un tri par population (croissant/décroissant)
  5. Gérez le chargement (loader) et les erreurs

Envoyer des données : POST, PUT et PATCH avec fetch

Une fois la requête GET maîtrisée, l’envoi de données suit la même logique mais demande trois éléments supplémentaires : la méthode HTTP, l’en-tête Content-Type et le corps sérialisé. À Dakar, lorsqu’un développeur intègre un formulaire de réservation pour un cabinet médical à Sicap Liberté, c’est exactement ce pattern qu’il met en place côté front pour parler à son backend Node.js ou Laravel.

const reponse = await fetch('https://api.exemple.sn/rendezvous', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
  body: JSON.stringify({ patient: 'Aïssatou Ndiaye', date: '2026-05-12T10:00:00+00:00', motif: 'consultation' })
});
if (!reponse.ok) { throw new Error(`Erreur HTTP ${reponse.status}`); }
const donnees = await reponse.json();

Un point souvent oublié : fetch() ne lève pas d’exception sur un statut 4xx ou 5xx. Vous devez tester reponse.ok manuellement, sinon votre code traitera une erreur 500 comme un succès.

Authentification : Bearer token, API key et cookies

Trois schémas dominent les API REST en 2026. Le plus courant reste le Bearer token JWT, transmis dans l’en-tête Authorization. Voici comment l’attacher proprement à toutes vos requêtes via une fonction utilitaire :

function fetchAuth(url, options = {}) {
  const token = localStorage.getItem('access_token');
  return fetch(url, { ...options, headers: { ...options.headers, 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } });
}

Pour une API key (clé fournie par Wave, Orange Money ou un fournisseur SMS), placez-la dans un en-tête personnalisé comme X-API-Key. Pour les cookies de session classique, ajoutez credentials: 'include' sinon le navigateur n’enverra rien sur les requêtes cross-origin.

Timeout et annulation : AbortSignal en pratique

Sur les connexions Orange ou MTN Côte d’Ivoire, une requête peut traîner 30 secondes avant qu’un proxy intermédiaire ne la coupe. Depuis 2022 et désormais stable dans tous les navigateurs majeurs, AbortSignal.timeout() résout ça en une ligne :

try {
  const reponse = await fetch('https://api.exemple.ci/produits', { signal: AbortSignal.timeout(5000) });
  const produits = await reponse.json();
} catch (err) {
  if (err.name === 'TimeoutError') afficherMessage('La connexion est lente, réessayez.');
}

Pour combiner un timeout et une annulation manuelle, AbortSignal.any() fusionne plusieurs signaux.

Retry avec backoff exponentiel

Une API instable n’est pas une fatalité. Un retry intelligent transforme 80 % des erreurs 502/503 transitoires en succès silencieux. La règle : doubler le délai à chaque tentative, abandonner après 3 essais, ne jamais retry une 4xx.

async function fetchAvecRetry(url, options = {}, maxEssais = 3) {
  for (let essai = 0; essai < maxEssais; essai++) {
    try {
      const reponse = await fetch(url, options);
      if (reponse.ok) return reponse;
      if (reponse.status >= 400 && reponse.status < 500) throw new Error(`Erreur client`);
    } catch (err) { if (essai === maxEssais - 1) throw err; }
    await new Promise(r => setTimeout(r, Math.pow(2, essai) * 1000));
  }
}

Streamer une grosse réponse JSON

Lorsqu’une API renvoie 50 000 produits d’un coup, charger tout en mémoire fait planter les téléphones d’entrée de gamme vendus à Lomé ou Cotonou. La solution : lire le flux par morceaux avec response.body.getReader().

const reponse = await fetch('https://api.exemple.tg/catalogue.json');
const total = +reponse.headers.get('Content-Length');
const reader = reponse.body.getReader();
let recu = 0; const chunks = [];
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  chunks.push(value); recu += value.length;
}
const texte = await new Blob(chunks).text();
const data = JSON.parse(texte);

CORS : comprendre et débloquer les erreurs

Vous voyez en console : Access to fetch has been blocked by CORS policy. Ce n’est pas un bug, c’est la sécurité qui fonctionne. Le serveur cible doit explicitement autoriser votre origine via l’en-tête Access-Control-Allow-Origin. Trois solutions : ajouter l’en-tête côté serveur (Express middleware cors, Laravel config/cors.php) ; créer un proxy Cloudflare Workers ou Vercel Functions ; configurer le proxy Vite/webpack-dev-server en dev.

Erreurs courantes et comment les éviter

Symptôme Cause Correction
response.json() lance SyntaxError Le serveur renvoie du HTML d’erreur Vérifier reponse.ok et Content-Type
Body envoyé vide en POST Oubli de JSON.stringify() Toujours sérialiser les objets
Cookie pas envoyé credentials par défaut = same-origin Ajouter credentials: ‘include’
FormData + Content-Type JSON Conflit d’en-tête Laisser fetch deviner pour FormData

Sur le même thème sur les patterns frontend modernes adaptés au contexte ouest-africain, voir aussi le guide événements JavaScript et le guide media queries responsive. Tester systématiquement sur Galaxy A03 ou équivalent à 200 EUR avec 4G locale dégradée reste le seul juge fiable de la performance perçue par la majorité des visiteurs locaux.

Dans la continuité sur les patterns frontend modernes adaptés au contexte ouest-africain, voir aussi le guide événements JavaScript et le guide media queries responsive. Tester systématiquement sur Galaxy A03 ou équivalent à 200 EUR avec 4G locale dégradée reste le seul juge fiable de la performance perçue par la majorité des visiteurs locaux.

Pour étoffer le tableau sur les patterns frontend modernes adaptés au contexte ouest-africain, voir aussi le guide événements JavaScript et le guide media queries responsive. Tester systématiquement sur Galaxy A03 ou équivalent à 200 EUR avec 4G locale dégradée reste le seul juge fiable de la performance perçue par la majorité des visiteurs locaux.

Pour approfondir sur les patterns frontend modernes adaptés au contexte ouest-africain, voir aussi le guide événements JavaScript et le guide media queries responsive. Tester systématiquement sur Galaxy A03 ou équivalent à 200 EUR avec 4G locale dégradée reste le seul juge fiable de la performance perçue par la majorité des visiteurs locaux.

Adaptation au contexte ouest-africain : performance, équipe, marché

Pour un développeur basé à Dakar, Abidjan, Bamako, Cotonou, Lomé, Ouagadougou, Niamey ou Conakry qui livre des sites web ou applications custom à des PME locales, trois adaptations pèsent sur le succès des projets. Premièrement, la connectivité 4G inégale impose de réduire le poids des pages au strict nécessaire — un site qui charge en 6 secondes perd 40 % de ses visiteurs avant l’affichage. Deuxièmement, le profil typique des développeurs disponibles sur le marché local est majoritairement formé sur du JavaScript moderne et du PHP, avec une expertise variable sur les outils plus avancés (TypeScript, frameworks edge, design systems). Adapter la pile technique au profil d’équipe disponible sur place évite la dette technique liée au turn-over.

Troisièmement, le coût en FCFA des services cloud doit être anticipé dans le budget. Hetzner CX22 à 4 500 FCFA/mois reste imbattable pour un démarrage, Cloudflare Pages gratuit pour les sites statiques, Backblaze B2 à 6 USD/TB/mois pour les sauvegardes. Pour les projets B2C qui exigent une latence faible, héberger sur un CDN avec PoP africain (Cloudflare Lagos, Africa Data Centres) divise par trois la latence perçue par rapport à un déploiement européen sans CDN.

Tester sur appareils réels avant la mise en production

Plus important que tous les outils synthétiques, tester son site sur un Android d’entrée de gamme avec une connexion 4G locale dégradée donne le seul verdict qui compte. Galaxy A03 à 200 EUR neuf, Tecno Spark, Itel A60 sont les appareils dominants chez les visiteurs ouest-africains. Sur ces téléphones, un site qui semble rapide sur DevTools peut être laggy en réalité. Trois conseils pratiques. Premièrement, garder un appareil de test physique à portée de main pour vérifier chaque livraison majeure. Deuxièmement, profiler avec Chrome DevTools en mode Slow 4G + 4x CPU slowdown pour simuler ces conditions. Troisièmement, mesurer en conditions réelles avec PageSpeed Insights qui agrège les données du Chrome User Experience Report sur 28 jours — la donnée terrain qui compte vraiment.

Pour explorer plus loin

مشاركة