בקשות ותשובות לעומק

כל Route ב-Express מקבל שני אובייקטים: req (מה שהלקוח שלח) ו-res (מה שנחזיר לו). בשיעורים הקודמים השתמשנו בהם בקטנה. כאן נעבור על כל מה שבאמת צריך כדי לבנות API.

ארבעה מקורות למידע בבקשה

מקורדוגמה בכתובת/בבקשהאיך קוראים
פרמטר בנתיב/users/42req.params.id
Query String/search?q=ספר&page=2req.query.q
גוף הבקשהJSON שנשלח ב-POSTreq.body
HeadersAuthorization: Bearer ...req.get('Authorization')

פרמטרים בנתיב

app.get('/users/:userId/posts/:postId', (req, res) => {
    const { userId, postId } = req.params;
    // כל הפרמטרים מגיעים כמחרוזות
    res.json({ userId: Number(userId), postId: Number(postId) });
});

פרמטר אופציונלי ב-Express 5 נכתב בסוגריים מסולסלים: '/files{/:name}'.

Query String

// GET /products?category=books&page=2
app.get('/products', (req, res) => {
    const category = req.query.category ?? 'all';
    const page = Math.max(1, Number(req.query.page) || 1);
    res.json({ category, page });
});

אל תסמכו על מה שמגיע ב-Query: הוא יכול להיות חסר, ריק או מחרוזת במקום מספר. תמיד ברירת מחדל והמרה.

גוף הבקשה

Express לא קורא את הגוף לבד. צריך Middleware שיודע לפענח אותו, פעם אחת בראש האפליקציה:

app.use(express.json());                          // JSON (API)
app.use(express.urlencoded({ extended: false }));  // טפסי HTML רגילים

app.post('/api/contact', (req, res) => {
    const { name, email } = req.body;
    if (!name || !email) {
        return res.status(400).json({ error: 'שם ואימייל הם שדות חובה' });
    }
    res.status(201).json({ received: { name, email } });
});

שכחתם את express.json()? req.body יהיה undefined. זו הטעות הכי נפוצה בשיעור הזה.

Headers ומידע נוסף על הבקשה

app.get('/whoami', (req, res) => {
    res.json({
        method: req.method,              // 'GET'
        path: req.path,                  // '/whoami'
        userAgent: req.get('User-Agent'),
        ip: req.ip,
        wantsJson: req.accepts('json'),
    });
});

סוגי תשובות

res.send('טקסט או HTML');              // Content-Type לפי התוכן
res.json({ ok: true });                  // JSON
res.status(201).json(newItem);           // קוד סטטוס + JSON
res.sendStatus(204);                     // רק קוד סטטוס, בלי גוף
res.redirect('/login');                  // הפניה (302)
res.redirect(301, '/new-address');       // הפניה קבועה
res.sendFile(path.join(import.meta.dirname, 'report.pdf'));
res.download('./files/invoice.pdf');     // מוריד כקובץ

כלל ברזל: תשובה אחת לכל בקשה

// ❌ באג: אם אין שם, נשלחות שתי תשובות
app.post('/api/users', (req, res) => {
    if (!req.body.name) {
        res.status(400).json({ error: 'חסר שם' });
    }
    res.status(201).json({ ok: true });
});

// ✅ return עוצר את הפונקציה אחרי התשובה הראשונה
app.post('/api/users', (req, res) => {
    if (!req.body.name) {
        return res.status(400).json({ error: 'חסר שם' });
    }
    res.status(201).json({ ok: true });
});

הגרסה הראשונה זורקת Cannot set headers after they are sent to the client. כשרואים את השגיאה הזו, מחפשים תשובה שנשלחת פעמיים.

Headers ו-Cookies בתשובה

res.set('Cache-Control', 'no-store');
res.cookie('theme', 'dark', { maxAge: 1000 * 60 * 60 * 24 * 30, httpOnly: true, sameSite: 'lax' });
res.clearCookie('theme');

דוגמה מלאה להרצה

בקובץ req-res.js יש נתיב לכל אחד מהנושאים כאן. הריצו אותו ונסו בדפדפן את /search?q=test, /headers ו-/status/404.

בדקו את עצמכם

נסו לענות לבד לפני שאתם פותחים את התשובה.

  1. איפה נמצא הערך מהכתובת /search?q=ספר?

    1. req.params.q
    2. req.headers.q
    3. req.body.q
    4. req.query.q
    הצגת התשובה

    תשובה ד. params לחלקי הנתיב, query ל-Query String, body לגוף הבקשה.

  2. מה גורם לשגיאה "Cannot set headers after they are sent"?

    1. JSON לא תקין
    2. חסר express.json()
    3. שליחת שתי תשובות לאותה בקשה
    4. פורט תפוס
    הצגת התשובה

    תשובה ג. כותבים return לפני כל res.json מוקדם.

  3. איזה קוד סטטוס מתאים אחרי יצירה מוצלחת?

    1. 201
    2. 200
    3. 302
    4. 204
    הצגת התשובה

    תשובה א. 201 Created, ובדרך כלל מחזירים גם את המשאב החדש.