Routes asynchrones
Fait partie de la section Les bases d’Express du Journey Node.js de Coddy. Leçon 21 sur 23.
Un tableau disparaît lorsque le serveur redémarre. Un fichier conserve les données.
import { readFile } from 'node:fs/promises';
app.get('/api/forecast', async (req, res) => {
const text = await readFile('data/forecast.json', 'utf8');
res.json(JSON.parse(text));
});Une route peut être une fonction async qui attend avec await, ici readFile de node:fs/promises. Express 5 surveille la promesse renvoyée par la route : si elle est rejetée, parce que le fichier est introuvable ou que son JSON est invalide, l’erreur est transmise à votre gestionnaire d’erreurs, comme lors d’un throw.
Sans await, vous obtenez une promesse au lieu des données, et res.json(promise) envoie {}. Le nouveau fichier data/recipes.json contient les quatre recettes.
Défi
FacileUn tableau en mémoire est perdu chaque fois que le serveur redémarre. Conserve plutôt les recettes dans un fichier : data/recipes.json, qui contient déjà les quatre recettes.
- Dans
routes/recipes.js, importereadFileetwriteFiledepuisnode:fs/promises, et cesse d’importerrecipesdepuis../data.js. - Écris une fonction utilitaire,
loadRecipes(), une fonction asynchrone qui litdata/recipes.jsonet renvoie la liste analysée. Les prochaines leçons l’utiliseront sous ce nom. - Rends chaque route du fichier
async, et commence chacune en attendantloadRecipes():/tags, la liste, une recette, la route POST et la route de mise à l’échelle. - Dans la route POST, après avoir ajouté la nouvelle recette à la liste, réécris toute la liste dans
data/recipes.jsonavecwriteFile, sous forme de texte JSON. - La route de mise à l’échelle lance toujours une exception pour une recette sans nombre de portions, maintenant depuis une fonction asynchrone. Express 5 transmet toujours cette erreur à ton gestionnaire d’erreurs comme auparavant ; la réponse reste donc le code 500 et
{"error": "Something went wrong"}. Ne l’intercepte pas dans la route.
Essaie : Appuie sur « Jouer » et envoie /api/recipes : les mêmes quatre recettes, maintenant lues depuis le fichier. Choisis POST et envoie {"title": "Gazpacho", "tags": ["vegan", "soup"]} à /api/recipes : code 201 et recette avec "id": 5. Envoie /api/recipes/5 : Gazpacho. Envoie /api/recipes/99 : code 404. Ensuite, envoie POST {"title": "Toast"}, qui n’a pas de nombre de portions et devient la recette 6, puis envoie /api/recipes/6/scale?by=2 : code 500 et {"error":"Something went wrong"}. Si tu vois {} au lieu de tes données, il manque un await.
Essayez vous-même
import express from 'express';
import { recipes } from '../data.js';
const router = express.Router();
// Chaque tag, une seule fois, par ordre alphabétique.
router.get('/tags', (req, res) => {
const tags = new Set(recipes.flatMap((recipe) => recipe.tags));
res.json([...tags].sort());
});
router.get('/', (req, res) => {
res.json(recipes);
});
router.get('/:id', (req, res) => {
const recipe = recipes.find((r) => r.id === Number(req.params.id));
if (!recipe) {
res.status(404).json({ error: 'No such recipe' });
return;
}
res.json(recipe);
});
router.post('/', (req, res) => {
const { title, tags } = req.body;
if (!title) {
res.status(400).json({ error: 'A recipe needs a title' });
return;
}
const recipe = { id: recipes.length + 1, title, tags: tags ?? [] };
recipes.push(recipe);
res.status(201).json(recipe);
});
router.get('/:id/scale', (req, res) => {
const recipe = recipes.find((r) => r.id === Number(req.params.id));
if (!recipe) {
res.status(404).json({ error: 'No such recipe' });
return;
}
const by = Number(req.query.by);
if (!Number.isFinite(by) || by <= 0) {
res.status(400).json({ error: 'Scale by a positive number' });
return;
}
if (!recipe.servings) {
throw new Error(`Recipe ${recipe.id} has no servings`);
}
res.json({ title: recipe.title, servings: recipe.servings * by });
});
export default router;
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Les bases d’Express
Entraînez-vous par vous-même : Playground Express