Client, serveur et base de données

Le navigateur recueille les actions et affiche les résultats. PHP reçoit les requêtes, valide les données et interroge MySQL. MySQL conserve les enregistrements. Le navigateur ne reçoit pas les identifiants de connexion SQL : il parle à une API, pas directement à la base.

Une API est ici un ensemble d’adresses, de méthodes, de données attendues et de réponses. Le contrat permet à deux programmes d’être développés séparément tout en se comprenant. « Envoyer une tâche » doit préciser le nom du champ, son type, la méthode et la réponse de réussite.

SituationMéthode de notre APIRéponse attendue
Lire la liste.GETTableau JSON, même si la liste est vide.
Ajouter une tâche.POSTTâche créée avec son identifiant.
Changer un champ.PATCHTâche mise à jour.
Supprimer une tâche.DELETEConfirmation selon le contrat.

Objets, tableaux et valeurs JSON

{"id": 3, "title": "Préparer les photos", "done": false}

Un objet associe des noms de propriétés à des valeurs. Les noms et les chaînes sont entre guillemets doubles. Un tableau utilise []. false est un booléen ; "false" est une chaîne ; null signale une absence explicitement prévue. JSON ne contient pas de fonction, de commentaire ni de virgule finale. Il s’agit d’un format de données, pas d’un programme exécuté.

Statut, en-têtes et corps HTTP

Le statut décrit le résultat de la requête ; les en-têtes précisent des informations comme le format ; le corps contient les données. Content-Type: application/json annonce le format du corps. Accept exprime le format souhaité par le client. Un statut 200 ne garantit pas que le corps possède les champs attendus : le client doit aussi contrôler sa forme.

404 et une liste vide ne signifient pas la même chose. Une liste vide peut être une lecture réussie ; une adresse inconnue produit une erreur de ressource. Dans notre API, 422 désigne des données analysables mais refusées par les règles. Ne choisissez pas un statut uniquement pour rendre un message vert ou rouge.

Le trajet d’une requête HTTP

Dans le TP du semestre, le navigateur affiche une liste de tâches. PHP décide quelles données lire et MySQL les conserve. Le navigateur ne connaît ni le mot de passe MySQL ni le code PHP exécuté : il échange des requêtes et des réponses HTTP avec le serveur. Une API constitue un contrat : une adresse, une méthode, un format et des statuts annoncés.

Imaginez un restaurant : le client formule une demande ; le serveur la transmet ; la cuisine prépare le résultat. Ce schéma aide à distinguer les rôles, mais une requête HTTP reçoit une réponse, même quand la préparation échoue. La base de données n’est pas directement accessible depuis fetch().

GET /api/todos.php HTTP/1.1
Host: localhost:8080
Accept: application/json
[
  { "id": 1, "title": "Préparer les photos", "done": false },
  { "id": 2, "title": "Écrire la légende", "done": true }
]

Résultat attendu : un tableau JSON contient deux objets. Les noms des propriétés sont entre guillemets doubles ; false est un booléen, pas la chaîne "false". JSON transporte des valeurs, pas une fonction JavaScript. Un texte JSON doit être analysé avant d’utiliser ses propriétés.

Suivre une requête et sa réponse — simulation

<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;
      }

      pre {
        white-space: pre-wrap;
        overflow-wrap: anywhere;
      }
    </style>

<h1>Navigateur → PHP → MySQL → réponse</h1>
    <p>
      Simulation pédagogique : aucun serveur PHP ni base ne s’exécute dans cet
      aperçu.
    </p>
    <label for="scenario">Scénario</label>
    <select id="scenario">
      <option value="ok">Lecture réussie</option>
      <option value="invalid">Titre refusé</option>
      <option value="server">Base indisponible</option>
    </select>
    <button id="suivant">Étape suivante</button>
    <button id="reset">Recommencer</button>
    <ol id="trajet"></ol>
    <pre id="sortie" aria-live="polite"></pre>

    <script>
      const scenarios = {
        ok: [
          "Navigateur : GET /api/todos.php",
          "PHP : connexion PDO",
          "MySQL : SELECT id, title, done FROM todos",
          "PHP : réponse 200 application/json",
          "Navigateur : JSON analysé, puis DOM construit",
        ],
        invalid: [
          "Navigateur : POST avec un titre vide",
          "PHP : validation refusée avant SQL",
          "PHP : réponse 422 application/json",
          "Navigateur : message d’erreur ; saisie conservée",
        ],
        server: [
          "Navigateur : GET /api/todos.php",
          "PHP : connexion PDO impossible",
          "PHP : réponse 500 application/json",
          "Navigateur : message général et possibilité de réessayer",
        ],
      };
      let index = 0;
      const trajet = document.querySelector("#trajet");
      const sortie = document.querySelector("#sortie");
      function reset() {
        index = 0;
        trajet.replaceChildren();
        sortie.textContent = "";
      }
      document.querySelector("#scenario").addEventListener("change", reset);
      document.querySelector("#reset").addEventListener("click", reset);
      document.querySelector("#suivant").addEventListener("click", () => {
        const scenario = document.querySelector("#scenario").value;
        const etapes = scenarios[scenario];
        if (index >= etapes.length) return;
        const li = document.createElement("li");
        li.textContent = etapes[index++];
        trajet.append(li);
        if (index === etapes.length) {
          sortie.textContent =
            scenario === "ok"
              ? '[{"id":1,"title":"Préparer les photos","done":false}]'
              : '{"error":"Opération impossible"}';
        }
      });
    </script>

Le panneau Réseau du navigateur

Dans F12 → Réseau, observez l’adresse, la méthode, le statut, les en-têtes et la réponse. 200 indique ici une lecture réussie ; 201 une création ; 400 une requête mal formée ; 404 une ressource absente ; 422 des données valides en JSON mais refusées par les règles du projet ; 500 une erreur du serveur. Le statut n’est pas la note de l’étudiant ! Content-Type décrit le corps envoyé ; Accept exprime le format souhaité par le client.

Les méthodes choisies pour notre contrat seront GET pour lire, POST pour créer, PATCH pour modifier certains champs et DELETE pour supprimer. Ce sont des conventions de cette API, qu’il faut respecter des deux côtés. GET ne doit pas supprimer une tâche au simple chargement d’un lien.

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

NotionRègle à retenir
ClientLe navigateur est le client qui demande et affiche les données.
ServeurPHP traite la requête côté serveur.
StockageMySQL conserve les données persistantes.
APIUne API définit un contrat d’échange.
LectureGET sert à lire dans le contrat de notre API.
CréationPOST crée une tâche dans notre contrat.
ModificationPATCH modifie certains champs d’une tâche.
SuppressionDELETE supprime une tâche ciblée.
Lecture réussieHTTP 200 est le statut d’une lecture réussie ici.
Création réussieHTTP 201 confirme une création réussie.
IntrouvableHTTP 404 indique une ressource absente.
Données refuséesHTTP 422 signale des données analysables mais refusées par les règles.
Erreur serveurHTTP 500 indique une erreur interne du serveur.
Format envoyéContent-Type décrit le format du corps envoyé.
Format souhaitéAccept exprime le format souhaité par le client.
Objet JSONUn objet JSON associe des noms de propriétés à des valeurs.
Tableau JSONLa liste JSON est un tableau d’objets.
Booléenfalse sans guillemets est un booléen JSON.
Chaîne”false” avec guillemets est une chaîne, pas un booléen.
ObservationL’onglet Réseau montre méthode, statut, en-têtes et corps des échanges.