העלאה לאוויר (Deployment)

שרת שרץ רק על המחשב שלכם הוא עדיין תרגיל. בשיעור האחרון נכין את ה-API לפרודקשן ונעלה אותו לשירות אחסון, כך שכל אחד יוכל לפנות אליו מכתובת אמיתית.

להכין את הקוד

// src/server.js
import { app } from './app.js';

const port = Number(process.env.PORT) || 3000;   // שירותי אחסון קובעים את הפורט בעצמם
const server = app.listen(port, () => console.log(`השרת רץ בפורט ${port}`));

// כשהשירות מכבה או מחליף גרסה: לסיים בקשות פתוחות לפני יציאה
process.on('SIGTERM', () => {
    console.log('מקבל SIGTERM, סוגר את השרת...');
    server.close(() => process.exit(0));
});
  • PORT מהסביבה - אף פעם לא פורט קבוע בפרודקשן.
  • NODE_ENV=production - Express ורוב הספריות עובדים מהר יותר ומסתירים פרטים פנימיים.
  • Health check - נתיב פשוט שהשירות בודק כדי לדעת שהשרת חי:
app.get('/health', (req, res) => res.json({ status: 'ok' }));

package.json מוכן לפרודקשן

{
  "name": "todo-api",
  "type": "module",
  "engines": { "node": ">=22" },
  "scripts": {
    "dev": "node --watch --env-file=.env src/server.js",
    "start": "node src/server.js",
    "test": "node --test"
  }
}

שירותי אחסון מריצים npm install ואז npm start. בפרודקשן אין קובץ .env: את המשתנים מגדירים בממשק של השירות.

איפה לאחסן

סוגדוגמאותמתאים ל
פלטפורמה מנוהלת (PaaS)Render, Railway, Fly.ioהדרך הקלה: מחברים את ה-GitHub, והשירות בונה ומעלה בכל push
ServerlessVercel, Netlify Functions, Cloud FunctionsAPI קטן עם עומס לא קבוע. דורש התאמה של המבנה
שרת וירטואלי (VPS)DigitalOcean, Hetzner, AWS EC2שליטה מלאה, אבל אתם אחראים לעדכונים, HTTPS ואבטחה

העלאה לפלטפורמה מנוהלת, שלב אחר שלב

  1. דחפו את הקוד ל-GitHub (ודאו ש-.env ו-node_modules ב-.gitignore).
  2. בשירות, צרו "Web Service" חדש וחברו את המאגר.
  3. Build command: npm ci. Start command: npm start.
  4. הגדירו משתני סביבה: NODE_ENV=production, DATABASE_URL, JWT_SECRET.
  5. צרו מסד נתונים מנוהל (רוב השירותים מציעים PostgreSQL או MySQL), והעתיקו את כתובת החיבור ל-DATABASE_URL.
  6. אחרי ההעלאה, בדקו את /health בכתובת שקיבלתם.

npm ci מתקין בדיוק את מה שב-package-lock.json, ולכן חובה לשמור את הקובץ הזה ב-Git.

מאחורי Proxy

בכל השירותים האלה, הבקשה עוברת דרך Proxy לפני שהיא מגיעה לשרת שלכם. בלי הגדרה, Express יחשוב שכל הבקשות מגיעות מאותה כתובת, והגבלת הקצב תחסום את כולם יחד:

app.set('trust proxy', 1);  // סומכים על Proxy אחד לפני השרת

Docker: אותה סביבה בכל מקום

Docker אורז את השרת עם הגרסה המדויקת של Node.js, כך שהוא רץ אותו דבר אצלכם, אצל חבר לצוות ובשרת:

# Dockerfile
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY src ./src
ENV NODE_ENV=production
EXPOSE 3000
USER node
CMD ["node", "src/server.js"]
docker build -t todo-api .
docker run -p 3000:3000 --env-file .env todo-api
  • --omit=dev - בלי חבילות פיתוח, כדי שה-image יהיה קטן.
  • USER node - לא להריץ כ-root, שכבת הגנה נוספת.
  • קובץ .dockerignore עם node_modules ו-.env, כדי שהם לא ייכנסו ל-image.

אחרי שהשרת באוויר

  • לוגים - כל שירות מציג את מה ש-console.log מדפיס. לפרויקט רציני: ספריית לוגים כמו pino.
  • ניטור שגיאות - שירות כמו Sentry מתריע כשמשהו נשבר, עם כל הפרטים.
  • גיבויים - ודאו שמסד הנתונים מגובה אוטומטית.
  • עדכונים - npm audit ושדרוג תלויות באופן קבוע.

סיימתם את המסלול

עברתם מ"שלום עולם" ב-Node.js עד API מאובטח, עם מסד נתונים, משתמשים ושרת באוויר. זה הבסיס של רוב מערכות ה-Backend. הצעד הטבעי הבא: לחבר אליו ממשק ב-React, ולהעמיק ב-SQL.

בדקו את עצמכם

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

  1. מה תפקיד נתיב /health?

    1. לאפשר לשירות האחסון לבדוק שהשרת חי
    2. להתחבר
    3. להציג את הקוד
    4. לאתחל את המסד
    הצגת התשובה

    תשובה א. אם הבדיקה נכשלת, השירות מפעיל את השרת מחדש.

  2. למה app.set('trust proxy', 1) מאחורי שירות אחסון?

    1. כדי להצפין
    2. מהירות
    3. כדי לאפשר CORS
    4. כדי ש-req.ip יהיה כתובת הלקוח האמיתית, והגבלת הקצב תעבוד
    הצגת התשובה

    תשובה ד. בלי זה כל הבקשות נראות כאילו הגיעו מה-Proxy.

  3. למה npm ci בשרת ולא npm install?

    1. ci מתקין בדיוק לפי package-lock.json, התקנה זהה ומהירה
    2. אין הבדל
    3. ci מוחק את הקוד
    4. install לא עובד בשרת
    הצגת התשובה

    תשובה א. כך מה שנבדק אצלכם הוא מה שרץ בפרודקשן.