Rezept des Tages
Teil des Abschnitts Express-Grundlagen der Node.js-Journey von Coddy. Lektion 22 von 23.
Recipe Box kann vertraute Kenntnisse zu Routen und zum Lesen von Dateien nutzen, um für einen Tag ein Rezept bereitzustellen. Der bereitgestellte Helfer dayOfYear wandelt ein Datum in eine Zahl um: Der 1. Januar ist 1 und der 1. März 2026 ist 60. Ein fehlendes Datum bedeutet heute; nicht lesbarer Text gibt null zurück.
const day = dayOfYear(req.query.date);
// ?date=2026-03-01 ergibt 60
// ?date=soon ergibt nullVerwende den Restwertoperator, um eine Position auszuwählen: day % recipes.length. Bei vier Rezepten wählt der Tag 60 die Position null aus. Lade die Rezepte innerhalb der Anfrage, damit auch kürzlich veröffentlichte Rezepte berücksichtigt werden. Die Datumsanalyse ist bereits bereitgestellt; konzentriere dich darauf, die Route zusammenzusetzen.
Aufgabe
EinfachFüge Recipe Box einen Endpunkt für das Rezept des Tages hinzu. Die bereitgestellte Datei day-of-year.js exportiert dayOfYear(text), das die Tagesnummer oder bei einem ungültigen Datum null zurückgibt.
- Exportiere
loadRecipesausroutes/recipes.js. Importiere es inserver.js: Der Router bleibt der Standardimport, und{ loadRecipes }folgt darauf nach einem Komma. - Importiere
dayOfYear. Füge inserver.jsoberhalb des 404-Handlers eineasync-Route fürGET /api/todayhinzu. Übergibreq.query.datean die Hilfsfunktion. - Gib bei
nullden Statuscode 400 mit{"error":"Use a date like 2026-03-01"}zurück. - Rufe andernfalls
loadRecipes()mitawaitauf und gibrecipes[day % recipes.length]als JSON zurück. Behalte die vorhandenen Routen und den Fehler-Handler bei.
Probiere GET /api/today?date=2026-03-01 aus: Der erste Eintrag wird zurückgegeben, weil die Tagesnummer 60 beträgt und es vier Rezepte gibt. Probiere ?date=soon aus: Der oben angegebene 400-Fehler wird zurückgegeben.
Probier es selbst
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') sendet reinen Text, sodass jeder Titel in einer eigenen Zeile erscheint.
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');
});
Alle Lektionen in Express-Grundlagen
Übe selbstständig: Express-Playground