Menu
CoddyTech

La recette du jour

Fait partie de la section Les bases d’Express du Journey Node.js de Coddy. Leçon 22 sur 23.

Recipe Box peut utiliser des compétences familières en routage et en lecture de fichiers pour servir une recette par jour. L’assistant dayOfYear fourni convertit une date en nombre : le 1er janvier vaut 1 et le 1er mars 2026 vaut 60. Si la date est absente, la date du jour est utilisée ; si le texte est illisible, la valeur null est renvoyée.

const day = dayOfYear(req.query.date);
// ?date=2026-03-01 donne 60
// ?date=soon donne null

Utilise l’opérateur modulo pour choisir une position : day % recipes.length. Avec quatre recettes, le jour 60 sélectionne la position zéro. Charge les recettes dans la requête afin que les recettes publiées récemment soient également prises en compte. L’analyse de la date est déjà fournie ; concentre-toi sur la composition de la route.

challenge icon

Défi

Facile

Ajoute un point de terminaison « Recette du jour » à Recipe Box. Le fichier fourni day-of-year.js exporte dayOfYear(text), qui renvoie le numéro du jour ou null si la date est invalide.

  1. Exporte loadRecipes depuis routes/recipes.js. Importe-le dans server.js : le routeur reste l’importation par défaut, et { loadRecipes } vient après, séparé par une virgule.
  2. Importe dayOfYear. Dans server.js, au-dessus du gestionnaire 404, ajoute une route async pour GET /api/today. Passe req.query.date à la fonction utilitaire.
  3. Si la valeur est null, renvoie le code 400 avec {"error":"Use a date like 2026-03-01"}.
  4. Sinon, attends le résultat de loadRecipes() et renvoie recipes[day % recipes.length] au format JSON. Conserve les routes existantes et le gestionnaire d’erreurs.

Essaie GET /api/today?date=2026-03-01 : la première recette est renvoyée, car le numéro du jour est 60 et il y a quatre recettes. Essaie ?date=soon : l’erreur 400 ci-dessus est renvoyée.

Essayez vous-même

import { escapeHtml } from './html.js';
import express from 'express';
import { randomUUID } from 'node:crypto';
import { recipes } from './data.js';
import recipesRouter from './routes/recipes.js';
import reviewsRouter from './routes/reviews.js';

const app = express();

app.use((req, res, next) => {
    console.log(`${req.method} ${req.url}`);
    next();
});

app.use((req, res, next) => {
    res.set('X-Request-Id', randomUUID());
    next();
});

app.use(express.json());

app.use(express.static('public'));

app.get('/', (req, res) => {
    const items = recipes.map((r) => `<li><a href="/recipes/${r.id}">${escapeHtml(r.title)}</a></li>`);
    res.send(`<link rel="stylesheet" href="/style.css"><h1>Recipe Box</h1><ul>${items.join('')}</ul>`);
});

// type('text') envoie du texte brut, si bien que chaque titre apparaît sur sa propre ligne.
app.get('/recipes', (req, res) => {
    const { tag } = req.query;
    const list = tag ? recipes.filter((recipe) => recipe.tags.includes(tag)) : recipes;
    res.type('text').send(list.map((recipe) => recipe.title).join('\n'));
});

app.get('/recipes/:id', (req, res) => {
    const recipe = recipes.find((r) => r.id === Number(req.params.id));
    if (!recipe) {
        res.status(404).send('No such recipe');
        return;
    }
    res.send(recipe.title);
});

app.use('/api/recipes', recipesRouter);
app.use('/api/recipes/:id/reviews', reviewsRouter);

app.get('/about', (req, res) => {
    res.send('Recipe Box collects family recipes.');
});

app.get('/menu', (req, res) => {
    res.redirect(301, '/recipes');
});

app.get('/menu/:id', (req, res) => {
    res.redirect(301, `/recipes/${req.params.id}`);
});

app.use((req, res) => {
    if (req.path.startsWith('/api/')) {
        res.status(404).json({ error: 'Not found' });
        return;
    }
    res.status(404).send('<h1>Page not found</h1><p><a href="/">Back to Recipe Box</a></p>');
});

app.use((err, req, res, next) => {
    console.error(err);
    res.status(500).json({ error: 'Something went wrong' });
});

app.listen(3000, () => {
    console.log('Recipe Box is running on port 3000');
});

Toutes les leçons de Les bases d’Express

Entraînez-vous par vous-même : Playground Express