fetch et response.json

fetch(url) renvoie une promesse, c’est-à-dire une valeur représentant une opération encore en cours ou terminée. Dans une fonction async, await attend le résultat de cette promesse pour poursuivre la fonction. Il n’immobilise pas l’ensemble du navigateur.

async function charger() {
  const reponse = await fetch('/api/todos.php');
  if (!reponse.ok) throw new Error('Lecture refusée');
  const taches = await reponse.json();
  if (!Array.isArray(taches)) throw new Error('Liste attendue');
  return taches;
}

La première attente fournit un objet Response avec son statut. La seconde lit et analyse son corps comme JSON. Une réponse HTTP 404 ne fait pas automatiquement rejeter la promesse de Fetch ; ok est donc contrôlé. Un JSON mal formé peut faire échouer json() même après un statut de réussite.

FrontièreVérificationCe qu’elle distingue
Réseau.try / catch autour de l’opération.Serveur inaccessible ou échange interrompu.
HTTP.response.ok / statusRéponse reçue mais refusée.
Format.Analyse JSON.Corps lisible ou invalide.
Contrat.Type et champs attendus.Donnée JSON valable mais forme incorrecte.

Les états chargement, vide et erreur

Affichez un état de chargement avant la requête, une liste ou un état vide après réussite, puis un message utile en cas d’erreur. finally permet de terminer l’état de chargement quelle que soit l’issue. Ne retirez pas l’ancien contenu avant de savoir si le nouveau peut être affiché, sauf si ce comportement est clairement voulu.

L’origine HTTP

Une origine associe protocole, hôte et port. Un front servi par Apache et une API sur le même service évitent un échange inter-origines. localhost:4321 et localhost:8000 sont deux origines différentes. Une erreur CORS ne se résout pas en changeant au hasard le JSON : vérifiez d’abord où sont servis la page et l’API.

Les requêtes asynchrones avec fetch

fetch() lance une requête et renvoie une promesse. await suspend la fonction asynchrone jusqu’au résultat de cette promesse ; le navigateur continue à gérer les interactions. Une réponse HTTP 404 ne rejette pas automatiquement la promesse : testez response.ok. La conversion response.json() est elle aussi asynchrone et peut échouer si le serveur renvoie une page HTML.

async function lireTaches() {
  const etat = document.querySelector('#etat');
  etat.textContent = 'Chargement…';

  try {
    const response = await fetch('/api/todos.php');
    if (!response.ok) {
      throw new Error(`Lecture impossible (${response.status})`);
    }
    const taches = await response.json();
    afficherTaches(taches);
    etat.textContent = `${taches.length} tâche(s)`;
  } catch (error) {
    etat.textContent = error.message;
  }
}

Un chargement, une liste vide et une erreur sont trois états différents. L’interface doit les distinguer. Pour afficher un titre reçu, créez un élément et affectez textContent : le titre est du texte, même s’il contient des caractères ressemblant à des balises.

Transformer du JSON en cartes — données locales

<style>
      * {
        box-sizing: border-box;
      }
      body {
        margin: 0;
        padding: 20px;
        font-family: system-ui, sans-serif;
        color: #18324c;
        background: #f1f5f9;
      }
      button,
      input,
      select,
      textarea {
        font: inherit;
        max-width: 100%;
      }
      button {
        padding: 8px 12px;
        cursor: pointer;
      }
      input,
      select,
      textarea {
        padding: 8px;
      }
      :focus-visible {
        outline: 3px solid #2563eb;
        outline-offset: 3px;
      }
      article,
      .panel {
        padding: 16px;
        background: white;
        border-radius: 12px;
        min-width: 0;
      }
      .cards {
        display: grid;
        gap: 16px;
      }
      label {
        display: block;
        margin-block: 12px 4px;
      }
      h1 {
        font-size: 1.4rem;
      }
      h2 {
        font-size: 1.15rem;
      }
      p,
      h2,
      a {
        overflow-wrap: anywhere;
      }
      .note {
        font-size: 0.9rem;
      }
    </style>

<h1>Du texte JSON aux cartes</h1>
    <label for="json">Données JSON à analyser</label>
    <textarea id="json" rows="5" style="width: 100%">
[{"title":"Atelier photo"},{"title":"Rencontre musicale"}]</textarea
    >
    <button id="afficher">Analyser et afficher</button>
    <p id="etat" role="status"></p>
    <section id="cartes" class="cards"></section>

    <script>
      document.querySelector("#afficher").addEventListener("click", () => {
        const etat = document.querySelector("#etat");
        try {
          const data = JSON.parse(document.querySelector("#json").value);
          if (
            !Array.isArray(data) ||
            data.some((item) => !item || typeof item.title !== "string")
          ) {
            throw new Error(
              "Un tableau d’objets avec un titre texte est attendu.",
            );
          }
          const cartes = data.map((item) => {
            const article = document.createElement("article");
            const h2 = document.createElement("h2");
            h2.textContent = item.title;
            article.append(h2);
            return article;
          });
          document.querySelector("#cartes").replaceChildren(...cartes);
          etat.textContent = `${data.length} carte(s)`;
        } catch (error) {
          etat.textContent = error.message;
        }
      });
      document.querySelector("#afficher").click();
    </script>

La partie PHP et la même origine

<?php
header('Content-Type: application/json; charset=utf-8');
$taches = [
    ['id' => 1, 'title' => 'Préparer les photos', 'done' => false],
];
echo json_encode($taches, JSON_UNESCAPED_UNICODE);

Sortie attendue du serveur PHP : [{"id":1,"title":"Préparer les photos","done":false}]. Le navigateur reçoit le résultat, jamais ce code PHP. Placez index.html et api/todos.php dans le même projet et lancez C:\xampp\php\php.exe -S localhost:8080 -t . depuis son dossier. Sur macOS, utilisez php -S localhost:8080 -t . après installation de PHP. Ouvrez http://localhost:8080 ; ouvrir le fichier avec file:// n’exécute pas PHP.

Le site de cours Astro sur 4321 et le projet PHP sur 8080 sont deux origines différentes. Les exemples étudiants utilisent une URL relative dans le projet PHP, pour garder la même origine. N’utilisez pas mode: 'no-cors' comme réparation : une réponse opaque n’offre pas un JSON lisible à votre code.

Tableau de référence — notions et opérations

NotionRègle à retenir
Promessefetch renvoie une promesse.
Asynchronismeawait attend une promesse dans une fonction asynchrone.
Fonction asyncUne fonction utilisant await dans l’exemple est déclarée async.
Succès HTTPresponse.ok est vrai pour les statuts HTTP de 200 à 299.
404 et promesseUn statut HTTP 404 ne rejette pas automatiquement fetch.
Analyse JSONresponse.json analyse le corps JSON de façon asynchrone.
JSON invalideresponse.json peut échouer si le corps n’est pas du JSON valide.
Gestion d’erreurcatch traite les erreurs du bloc try.
ChargementL’état Chargement est présenté pendant l’attente.
Liste videUn tableau vide reçu avec succès est un état vide normal.
Texte sûrtextContent affiche un titre comme texte.
Création DOMcreateElement crée un élément dans le document.
En-tête PHPPHP annonce application/json par Content-Type.
Encodage PHPjson_encode transforme les données PHP en texte JSON.
Serveur PHPLe serveur PHP exécute le script et renvoie son résultat.
Fichier localOuvrir file:// n’exécute pas PHP.
Serveur intégréphp -S localhost:8080 -t . lance le serveur du projet local.
OrigineUne origine inclut notamment schéma, hôte et port.
URL relativeL’URL /api/todos.php est résolue sur l’origine de la page ouverte.
Réponse opaqueno-cors ne fournit pas une réponse JSON lisible à ce code.