Menu
CoddyTech

La ricetta del giorno

Fa parte della sezione Fondamenti di Express del percorso Node.js di Coddy. Lezione 22 di 23.

Recipe Box può usare competenze già note relative alle route e alla lettura dei file per offrire una ricetta al giorno. L’helper dayOfYear fornito converte una data in un numero: il 1º gennaio è 1 e il 1º marzo 2026 è 60. Se manca una data, si intende oggi; il testo illeggibile restituisce null.

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

Usa l’operatore resto per scegliere una posizione: day % recipes.length. Con quattro ricette, il giorno 60 seleziona la posizione zero. Carica le ricette all’interno della richiesta, così anche quelle pubblicate di recente vengono incluse. L’analisi delle date è già fornita; concentrati sulla composizione della route.

challenge icon

Sfida

Facile

Aggiungi un endpoint Ricetta del giorno a Recipe Box. Il file fornito day-of-year.js esporta dayOfYear(text), che restituisce il numero del giorno oppure null per una data non valida.

  1. Esporta loadRecipes da routes/recipes.js. Importalo in server.js: il router rimane l’importazione predefinita e { loadRecipes } lo segue dopo una virgola.
  2. Importa dayOfYear. In server.js, sopra il gestore 404, aggiungi una route async per GET /api/today. Passa req.query.date alla funzione helper.
  3. Per null, restituisci 400 con {"error":"Use a date like 2026-03-01"}.
  4. Altrimenti, attendi loadRecipes() e restituisci recipes[day % recipes.length] come JSON. Mantieni le route e il gestore degli errori esistenti.

Prova GET /api/today?date=2026-03-01: restituisce la prima ricetta perché il numero del giorno è 60 e ci sono quattro ricette. Prova ?date=soon: restituisce l’errore 400 qui sopra.

Provalo tu

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') invia testo semplice, quindi ogni titolo appare su una riga a sé.
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');
});

Tutte le lezioni di Fondamenti di Express

Esercitati da solo: Playground Express