Question 1Après l'exécution de const response = await fetch(url);, que contient response ?
fetch — récupérer des données depuis une API
fetch et json() pour récupérer du JSON depuis une URL, response.ok pour repérer un échec, les URL relatives et Promise.all pour charger deux fichiers à la fois.
Jusqu'ici, les exercices écrivaient leurs données directement dans le code. Sur un vrai site, les prix et les stocks changent : la page interroge donc l'API du serveur (une URL que les programmes utilisent pour obtenir des données) à chaque ouverture.
Cet article présente fetch, qui récupère des données depuis une URL, et response.ok, qui indique si la requête a réussi.
Récupérer le JSON d'une API — fetch et json()
Imagine une page d'historique qui doit afficher les commandes enregistrées sur le serveur. Les commandes arrivent sous forme de chaîne JSON : il te faut donc un moyen d'attendre leur arrivée, puis une étape qui retransforme la chaîne en tableau. Dans les exercices, tu récupéreras, à la place d'une vraie API, des fichiers JSON placés dans le dossier fixtures de ce site.
fetch (une fonction qui demande au serveur les données situées à une URL) renvoie une Promise qui passe à fulfilled avec une Response, l'objet qui représente la réponse du serveur. Le code de statut status (un nombre qui vaut 200 en cas de succès) et les en-têtes (des informations sur la réponse, envoyées avant le corps) arrivent en premier, puis le corps suit : pour l'obtenir, tu fais donc un second await, sur response.json().
// Récupère le JSON des commandes stocké sur ce site (l'écriture de l'URL est expliquée dans la section suivante)
const response = await fetch("fixtures/fr/orders.json");
console.log(response.status); // 200
// Lit tout le corps et convertit le JSON en tableau
const orders = await response.json();
console.log(orders.length); // 3
console.log(orders[1].total); // 12600
// À partir d'ici, c'est un tableau ordinaire
const preparing = orders.filter((order) => order.status === "En préparation");
console.log(preparing.map((order) => order.id).join(", ")); // A-3002, A-3003
json() lit le corps jusqu'au bout et applique la même conversion que JSON.parse avant de renvoyer la valeur : tu attends donc le résultat avec await. Une fois converti, orders est un tableau ordinaire, sur lequel filter et map fonctionnent directement.
Afficher une Response donne {}
Dans la console des exercices, console.log(response) affiche {}, comme pour les éléments du DOM. Sans await, response.json() est une Promise, qui s'affiche elle aussi {}. Pour vérifier une Response, affiche son status ; pour le corps, affiche la valeur obtenue avec await sur json().
Écrire une URL à partir de la page — les URL relatives
Dans la section précédente, il suffisait d'écrire "fixtures/fr/orders.json" pour récupérer le fichier. Une URL complète qui commence par https:// inclut le domaine (la partie après https://, comme runner.desktechlearn.com, qui identifie le site) : il faudrait donc la réécrire chaque fois que le site change d'adresse.
Une URL relative (une URL écrite par rapport à l'URL de la page courante) part du dossier de la page quand elle ne commence pas par /, et de la racine du domaine quand elle commence par /. Le code des exercices s'exécute dans runner.html, une page invisible hébergée sur runner.desktechlearn.com, et non dans la page de l'article.
// URL de la page où s'exécute ce code (la console des exercices)
// https://runner.desktechlearn.com/tools/js-editor/runner.html
// Sans / au début : on part de js-editor, le dossier qui contient runner.html
const fromPage = await fetch("fixtures/fr/orders.json");
console.log(fromPage.url); // url est l'URL réellement demandée
// https://runner.desktechlearn.com/tools/js-editor/fixtures/fr/orders.json
// Avec / au début : on part de la racine du domaine
const fromRoot = await fetch("/tools/js-editor/fixtures/fr/orders.json");
console.log(fromRoot.url === fromPage.url); // true (les deux désignent le même fichier)
// Si tu oublies fixtures/fr/, la recherche se fait directement dans js-editor
const noFolder = await fetch("orders.json");
console.log(noFolder.url);
// https://runner.desktechlearn.com/tools/js-editor/orders.json
- Les URL qui commencent par
/partent d'ici
runner.html— la page où s'exécute le code des exercices- Les URL sans
/au début partent d'ici
- Contient
orders.json,users.jsonetproducts.json
Une URL qui commence par / désigne le même fichier, quel que soit le dossier de la page. Une URL sans / continue de fonctionner si tu déplaces la page et fixtures ensemble dans un autre dossier. Le tableau ci-dessous indique, pour chaque URL, son point de départ et si elle trouve le fichier.
| URL | Point de départ | Fichier trouvé ? |
|---|---|---|
| fixtures/fr/orders.json | js-editor | Oui |
| /tools/js-editor/fixtures/fr/orders.json | Racine du domaine | Oui (même fichier) |
| orders.json | js-editor | Non (absent de js-editor) |
Dans les DevTools, l'URL part de la page de l'article
Si tu ouvres la Console des DevTools sur la page de l'article et que tu exécutes fetch("fixtures/fr/orders.json"), le point de départ est l'URL de la page de l'article : la requête n'atteint donc jamais fixtures. Le serveur de la page de l'article renvoie le HTML de la page avec le code 200, même pour un chemin qui n'existe pas : la requête peut sembler avoir réussi, mais ce que tu obtiens n'est pas du JSON.
Repérer une requête qui a échoué — response.ok et status
Si tu fais une faute de frappe dans l'URL ou que le fichier a été supprimé, le serveur répond qu'il ne l'a pas trouvé. Une réponse arrive quand même, donc fetch passe à fulfilled avec une Response. Même dans un try / catch, l'exécution ne saute pas dans le catch : elle continue à la ligne suivante, celle qui lit le corps.
response.ok vaut true quand status est compris entre 200 et 299. 200 n'est pas le seul code de succès (201 en est un aussi) : utilise donc ok pour savoir si la requête a réussi. Pour un fichier absent, les serveurs renvoient en général 404 (Not Found), mais le serveur de la console des exercices renvoie 403 (Forbidden).
// Une URL avec une faute de frappe : order au lieu de orders
const response = await fetch("fixtures/fr/order.json");
// Même si le fichier est introuvable, fetch passe à fulfilled et renvoie une Response
console.log(response.ok); // false
console.log(response.status); // 404 ou 403 (selon le serveur ; la console des exercices renvoie 403)
// Convertit le corps en tableau seulement si ok vaut true
if (response.ok) {
const orders = await response.json();
console.log(`Commandes : ${orders.length}`);
} else {
console.log("Impossible de charger vos commandes"); // Impossible de charger vos commandes
}
Dans la colonne de droite, le corps est une description de l'erreur et non du JSON (du XML, dans la console des exercices) : json() lève donc une SyntaxError sur cette ligne. La colonne du milieu teste d'abord ok et affiche un message sans lire le corps : la requête échouée se termine par un message à l'écran plutôt que par une erreur.
Récupérer deux fichiers à la fois — fonctions async et Promise.all
Un écran d'administration qui affiche côte à côte le nombre de membres et le nombre de commandes a besoin de deux fichiers JSON, et aucun ne dépend de l'autre : tu les charges donc en parallèle (lancer plusieurs tâches en même temps, puis attendre), comme dans l'article sur async / await. Mais si tu appelles fetch deux fois et que tu passes les résultats à Promise.all, tu récupères deux Response, pas les corps.
Si tu regroupes tout, de fetch jusqu'à json(), dans une seule fonction async, la Promise renvoyée par son appel passe à fulfilled avec la valeur du corps. Si tu appelles cette fonction deux fois et que tu attends les deux appels avec Promise.all, tu obtiens un tableau des valeurs des corps, dans l'ordre où tu les as passés.
// Regroupe fetch jusqu'à json() et renvoie la valeur du corps (null si introuvable)
async function loadJson(path) {
const response = await fetch(path);
if (!response.ok) {
return null;
}
return await response.json();
}
// Si tu passes directement les appels à fetch, tu récupères des Response
const responses = await Promise.all([
fetch("fixtures/fr/products.json"),
fetch("fixtures/fr/orders.json"),
]);
console.log(responses[0].length); // undefined (ce n'est pas un tableau)
// Appeler loadJson deux fois lance les deux requêtes en même temps, et Promise.all rassemble les tableaux des corps
const [products, orders] = await Promise.all([
loadJson("fixtures/fr/products.json"),
loadJson("fixtures/fr/orders.json"),
]);
console.log(`Produits : ${products.length} / Commandes : ${orders.length}`); // Produits : 4 / Commandes : 3
Si une seule des Promises passées est rejected, Promise.all échoue sans transmettre aucun des autres résultats. loadJson passe à fulfilled avec null quand ok vaut false : si un fichier manque, tu récupères quand même l'autre tableau.
Vérification des connaissances
Répondez à chaque question une par une.
Question 2Avec un chemin qui n'existe pas, quel est l'état juste après l'exécution de const res = await fetch(url); ?
Question 3Dans du code qui s'exécute dans /tools/js-editor/runner.html, où fetch("users.json") va-t-il chercher le fichier ?