Menu
CoddyTech

Resep Hari Ini

Termasuk bagian Dasar-Dasar Express dalam perjalanan Node.js di Coddy. Pelajaran 22 dari 23.

Recipe Box dapat menggunakan keterampilan rute dan membaca file yang sudah kamu kenal untuk menyajikan satu resep setiap hari. Fungsi dayOfYear yang disediakan mengubah tanggal menjadi angka: 1 Januari adalah 1 dan 1 Maret 2026 adalah 60. Tanggal yang tidak dicantumkan berarti hari ini, teks yang tidak dapat dibaca menghasilkan null.

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

Gunakan operator sisa untuk memilih posisi: day % recipes.length. Dengan empat resep, hari ke-60 memilih posisi nol. Muat resep di dalam permintaan agar resep yang baru diposting juga ikut. Penguraian tanggal sudah disediakan, fokuslah menyusun rutenya.

challenge icon

Tantangan

Mudah

Tambahkan endpoint Resep Hari Ini ke Recipe Box. day-of-year.js yang disediakan mengekspor dayOfYear(text), yang mengembalikan nomor hari atau null untuk tanggal yang tidak valid.

  1. Ekspor loadRecipes dari routes/recipes.js. Impor ke server.js: router tetap menjadi impor default, dan { loadRecipes } mengikutinya setelah koma.
  2. Impor dayOfYear. Di server.js, tambahkan rute async untuk GET /api/today di atas handler 404. Teruskan req.query.date ke fungsi pembantu.
  3. Untuk null, kembalikan status 400 dengan {"error":"Use a date like 2026-03-01"}.
  4. Jika tidak, gunakan await loadRecipes() lalu kembalikan recipes[day % recipes.length] sebagai JSON. Pertahankan rute dan handler error yang sudah ada.

Coba GET /api/today?date=2026-03-01: hasilnya resep pertama karena nomor harinya 60 dan ada empat resep. Coba ?date=soon: hasilnya error 400 di atas.

Coba sendiri

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') mengirim teks biasa, sehingga setiap judul muncul di baris tersendiri.
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');
});

Semua pelajaran di Dasar-Dasar Express

Latihan sendiri: Playground Express