Receta del día
Parte de la sección Conceptos básicos de Express del Journey de Node.js de Coddy. Lección 22 de 23.
Recipe Box puede usar las conocidas habilidades para gestionar rutas y leer archivos para ofrecer una receta para cada día. La función auxiliar dayOfYear proporcionada convierte una fecha en un número: el 1 de enero es 1 y el 1 de marzo de 2026 es 60. Si falta la fecha, se usa la de hoy; el texto ilegible devuelve null.
const day = dayOfYear(req.query.date);
// ?date=2026-03-01 da 60
// ?date=soon da nullUsa el operador de resto para elegir una posición: day % recipes.length. Con cuatro recetas, el día 60 selecciona la posición cero. Carga las recetas dentro de la solicitud para que también se incluyan las publicadas recientemente. El análisis de fechas ya está proporcionado; concéntrate en componer la ruta.
Desafío
FácilAñade un endpoint de receta del día a Recipe Box. El archivo proporcionado day-of-year.js exporta dayOfYear(text), que devuelve el número del día o null para una fecha no válida.
- Exporta
loadRecipesdesderoutes/recipes.js. Impórtalo enserver.js: el router sigue siendo la importación predeterminada y{ loadRecipes }va después de una coma. - Importa
dayOfYear. Enserver.js, encima del controlador 404, añade una rutaasyncparaGET /api/today. Pasareq.query.datea la función auxiliar. - Para
null, devuelve 400 con{"error":"Use a date like 2026-03-01"}. - De lo contrario, espera a que se resuelva
loadRecipes()y devuelverecipes[day % recipes.length]como JSON. Conserva las rutas existentes y el controlador de errores.
Prueba GET /api/today?date=2026-03-01: devuelve la primera receta porque el número del día es 60 y hay cuatro recetas. Prueba ?date=soon: devuelve el error 400 anterior.
Pruébalo tú mismo
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') envía texto sin formato, por lo que cada título aparece en su propia línea.
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');
});
Todas las lecciones de Conceptos básicos de Express
Practica por tu cuenta: Playground de Express