תחביר וצורת כתיבה

הגדרת הפרויקט: ES Modules

הקורס כותב import ו-export (ES Modules), התקן המודרני של JavaScript. כדי ש-Node.js יקרא כך את הקבצים, מוסיפים שורה אחת ל-package.json:

{
  "name": "my-server",
  "type": "module",
  "dependencies": {
    "express": "^5.1.0"
  }
}

בקוד ישן תפגשו את הסגנון הקודם, CommonJS: const express = require('express') ו-module.exports. הוא עדיין עובד, אבל בפרויקט חדש כדאי ES Modules. שני הבדלים שכדאי לזכור:

  • בייבוא קובץ מקומי כותבים את הסיומת: import router from './routes/users.js'.
  • אין __dirname. במקומו יש import.meta.dirname (Node.js 20.11 ומעלה).

מבנה בסיסי של אפליקציית Express

כל אפליקציית Express מתחילה באותו מבנה:

// First
// ==============================================
// 1. ייבוא התלויות (Imports)
// ==============================================
import express from 'express';

// ==============================================
// 2. יצירת האפליקציה
// ==============================================
const app = express();

// ==============================================
// 3. הגדרות (Configuration)
// ==============================================
const PORT = 3000;

// ==============================================
// 4. Middleware גלובלי
// ==============================================
app.use(express.json());

// ==============================================
// 5. הגדרת Routes
// ==============================================
app.get('/', (req, res) => {
    res.send('שלום עולם!');
});

// ==============================================
// 6. הפעלת השרת
// ==============================================
app.listen(PORT, () => {
    console.log(`השרת רץ בפורט ${PORT}`);
});

תבניות כתיבה נפוצות

1. הגדרת Route בסיסי

// Template: app.METHOD(PATH, HANDLER)

// הצורה הכי נפוצה - Arrow Function
app.get('/users', (req, res) => {
    res.json({ users: [] });
});

// אפשר גם עם function רגיל
app.get('/users', function(req, res) {
    res.json({ users: [] });
});

2. Route עם פרמטרים

// One פרמטר
app.get('/users/:id', (req, res) => {
    const userId = req.params.id;
    res.send(`משתמש: ${userId}`);
});

// מספר פרמטרים
app.get('/users/:userId/posts/:postId', (req, res) => {
    const { userId, postId } = req.params;
    res.json({ userId, postId });
});

// פרמטר אופציונלי: בסוגריים מסולסלים (Express 5)
// ב-Express 4 כתבו '/users/:id?' - התחביר הזה כבר לא עובד
app.get('/users{/:id}', (req, res) => {
    if (req.params.id) {
        res.send(`משתמש ספציפי: ${req.params.id}`);
    } else {
        res.send('כל המשתמשים');
    }
});

3. שימוש ב-Query String

// URL: /search?q=express&page=1
app.get('/search', (req, res) => {
    const { q, page } = req.query;
    res.json({
        searchTerm: q,        // 'express'
        currentPage: page     // '1'
    });
});

4. קבלת נתונים מ-Body

// Must use middleware לפרסור JSON
app.use(express.json());

app.post('/users', (req, res) => {
    const { name, email } = req.body;

    // יצירת משתמש חדש
    const newUser = {
        id: Date.now(),
        name,
        email
    };

    res.status(201).json(newUser);
});

סגנונות כתיבה

סגנון 1: Inline Handlers (מתאים לפרויקטים קטנים)

// כל הלוגיקה ישירות ב-route
app.get('/users', (req, res) => {
    const users = [
        { id: 1, name: 'יוסי' },
        { id: 2, name: 'דני' }
    ];
    res.json(users);
});

סגנון 2: פונקציות נפרדות (Controllers)

// הפרדה של הלוגיקה לפונקציות
const getUsers = (req, res) => {
    res.json({ users: [] });
};

const getUserById = (req, res) => {
    res.json({ user: { id: req.params.id } });
};

const createUser = (req, res) => {
    res.status(201).json({ user: req.body });
};

// רישום ה-routes
app.get('/users', getUsers);
app.get('/users/:id', getUserById);
app.post('/users', createUser);

סגנון 3: Route Chaining

// שרשור routes לאותו נתיב
app.route('/users')
    .get((req, res) => {
        res.json({ action: 'Get all users' });
    })
    .post((req, res) => {
        res.json({ action: 'Create user' });
    });

app.route('/users/:id')
    .get((req, res) => {
        res.json({ action: 'Get user by ID' });
    })
    .put((req, res) => {
        res.json({ action: 'Update user' });
    })
    .delete((req, res) => {
        res.json({ action: 'Delete user' });
    });

שליחת סוגי תשובות שונים

טקסט רגיל

app.get('/text', (req, res) => {
    res.send('זהו טקסט רגיל');
});

JSON

app.get('/json', (req, res) => {
    res.json({
        message: 'הצלחה',
        data: { id: 1, name: 'Test' }
    });
});

HTML

app.get('/html', (req, res) => {
    res.send('<h1>כותרת HTML</h1><p>פסקה</p>');
});

קובץ

app.get('/file', (req, res) => {
    res.sendFile('/path/to/file.pdf');
});

הורדת קובץ

app.get('/download', (req, res) => {
    res.download('/path/to/file.pdf', 'document.pdf');
});

הפניה (Redirect)

app.get('/old-page', (req, res) => {
    res.redirect('/new-page');
});

// עם קוד סטטוס ספציפי
app.get('/moved', (req, res) => {
    res.redirect(301, '/new-location');
});

קודי סטטוס HTTP

הגדרת קוד סטטוס לפני התשובה:

// הצלחה
res.status(200).json({ message: 'OK' });

// נוצר בהצלחה
res.status(201).json({ message: 'Created' });

// אין תוכן להחזיר
res.status(204).send();

// בקשה שגויה
res.status(400).json({ error: 'Bad Request' });

// לא מורשה
res.status(401).json({ error: 'Unauthorized' });

// אסור
res.status(403).json({ error: 'Forbidden' });

// לא נמצא
res.status(404).json({ error: 'Not Found' });

// שגיאת שרת
res.status(500).json({ error: 'Internal Server Error' });

קודים נפוצים:

קוד משמעות שימוש
200 OK בקשה הצליחה
201 Created משאב נוצר
204 No Content הצלחה ללא תוכן
400 Bad Request נתונים שגויים
401 Unauthorized נדרשת הזדהות
403 Forbidden אין הרשאה
404 Not Found לא נמצא
500 Server Error שגיאת שרת

הגדרות (app.set)

// Set view engine
app.set('view engine', 'ejs');

// Set views directory
app.set('views', './views');

// הגדרת פורט
app.set('port', process.env.PORT || 3000);

// קריאת הגדרה
const port = app.get('port');

מבנה תיקיות מומלץ

פרויקט קטן:

project/
├── server.js          # קובץ ראשי
├── package.json
└── public/            # קבצים סטטיים
    ├── css/
    ├── js/
    └── images/

פרויקט בינוני-גדול:

project/
├── src/
│   ├── app.js              # הגדרת האפליקציה
│   ├── server.js           # הפעלת השרת
│   ├── routes/             # הגדרת routes
│   │   ├── index.js
│   │   ├── users.js
│   │   └── products.js
│   ├── controllers/        # לוגיקה עסקית
│   │   ├── userController.js
│   │   └── productController.js
│   ├── middleware/         # middleware מותאם
│   │   ├── auth.js
│   │   └── validation.js
│   └── config/             # קבצי הגדרות
│       └── database.js
├── public/                 # קבצים סטטיים
├── views/                  # תבניות HTML
├── package.json
└── .env                    # משתני סביבה

Best Practices (שיטות עבודה מומלצות)

1. השתמשו ב-ES Modules וב-const

// ✅ מודרני: import, ו-const לכל מה שלא משתנה
import express from 'express';
const app = express();

// ❌ סגנון ישן: CommonJS ו-var
var express = require('express');

2. טפלו בשגיאות

ב-Express 5, שגיאה בתוך פונקציה אסינכרונית (או Promise שנדחה) מגיעה לבד ל-Middleware של השגיאות. עדיין כדאי לתפוס שגיאות צפויות במקום, כדי להחזיר תשובה ברורה:

// ✅ טוב
app.get('/users/:id', async (req, res) => {
    try {
        const user = await findUser(req.params.id);
        res.json(user);
    } catch (error) {
        res.status(500).json({ error: error.message });
    }
});

3. השתמשו ב-async/await

// ✅ Modern and clean
app.get('/data', async (req, res) => {
    const data = await fetchData();
    res.json(data);
});

// ❌ פחות קריא
app.get('/data', (req, res) => {
    fetchData()
        .then(data => res.json(data))
        .catch(err => res.status(500).json(err));
});

4. הפרד לוגיקה לקבצים

// routes/users.js
import express from 'express';
const router = express.Router();

router.get('/', getUsers);
router.post('/', createUser);

export default router;

// app.js
import userRoutes from './routes/users.js';
app.use('/users', userRoutes);

5. השתמשו במשתני סביבה

// ✅ Good
const PORT = process.env.PORT || 3000;

// ❌ Bad
const PORT = 3000; // קשיח

סיכום - תבנית מלאה

// ===== server.js =====
import express from 'express';
const app = express();

// === Middleware ===
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static('public'));

// === Routes ===
app.get('/', (req, res) => {
    res.json({ message: 'ברוכים הבאים!' });
});

app.get('/api/users', (req, res) => {
    res.json({ users: [] });
});

app.post('/api/users', (req, res) => {
    const { name, email } = req.body;
    res.status(201).json({ id: 1, name, email });
});

// === Error Handler ===
app.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).json({ error: 'שגיאה בשרת' });
});

// === Start Server ===
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`🚀 השרת רץ: http://localhost:${PORT}`);
});

← חזרה ליסודות | המשך לניתוב →

בדקו את עצמכם

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

  1. מה צריך להוסיף ל-package.json כדי לכתוב import express from 'express'?

    1. "type": "module"
    2. "express": "esm"
    3. "import": true
    4. שום דבר
    הצגת התשובה

    תשובה א. כך Node.js מבין את הקבצים כ-ES Modules.

  2. למה לקחת את הפורט מ-process.env.PORT?

    1. שירותי אחסון קובעים את הפורט בעצמם
    2. סגנון
    3. זה מהיר יותר
    4. Express דורש
    הצגת התשובה

    תשובה א. פורט קבוע בקוד לא יעבוד בפרודקשן.