# הגדלת API ב־Node.js מ־200 אלף למיליון משתמשים עם תגובות מהירות ב־60%

URL: https://shahid09.is-a.dev/he/engineering/scaling-nodejs-api-200k-to-1m-users/
Author: Muhammad Shahid, Senior AI Software Engineer & Backend Developer (https://shahid09.is-a.dev/)
Published: 2026-10-31

**תשובה קצרה:** הבקאנד של CV Maker – Resume Builder, אפליקציית Android ב־Google Play, גדל מ־200 אלף ליותר ממיליון משתמשים בזמן שזמן התגובה הממוצע של ה־API ירד בכ־60%. העבודה כללה אינדקסים ב־MongoDB, החלפת שאילתות חוזרות ב־aggregation pipelines, קאש לתשובות, pagination, ולידציה של בקשות וטיפול מרוכז בשגיאות, כשהשחרורים נשארו יציבים.

- **פרויקט:** CV Maker – Resume Builder ‏(Google Play)
- **צמיחה:** מ־200 אלף ליותר ממיליון משתמשים
- **מהירות:** זמן תגובה ממוצע מהיר בכ־60%
- **הפתרון:** אינדקסים, aggregation pipelines, קאש, pagination
- **טכנולוגיות:** Node.js,‏ Express,‏ MongoDB

## לאן הולך הזמן ב־API איטי ב־Node.js?

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

## מה אינדקסים משנים?

אינדקס מאפשר ל־MongoDB לקפוץ ישר למסמכים שהשאילתה צריכה במקום לקרוא את כל האוסף. מוסיפים אינדקסים מורכבים שמתאימים לסינון ולסדר המיון של השאילתות החמות ביותר, ובודקים עם `explain()` שכל שאילתה בודקת בערך כמה מסמכים שהיא מחזירה. זה בדרך כלל השיפור הגדול ביותר.

## למה להחליף שאילתות חוזרות ב־aggregation pipeline?

דפוס נפוץ הוא לשלוף רשימה, לעבור עליה בלולאה ב־Node.js ולהריץ עוד שאילתה לכל פריט. עם 50 פריטים אלה 51 סבבים מול מסד הנתונים. ‏aggregation pipeline עושה את הסינון, הצירוף והספירה בתוך MongoDB בבקשה אחת:

```js
const resumes = await Resume.aggregate([
  { $match: { userId } },
  { $sort: { updatedAt: -1 } },
  { $limit: 20 },
  { $lookup: { from: "templates", localField: "templateId", foreignField: "_id", as: "template" } },
  { $project: { title: 1, updatedAt: 1, "template.name": 1 } },
])
```

שמים את `$match` ו־`$limit` כמה שיותר מוקדם, כדי שהשלבים הבאים יעבדו על פחות מסמכים, ובוחרים עם `$project` רק את השדות שהאפליקציה מציגה.

## מה כדאי לשמור בקאש?

תשובות שזהות להרבה משתמשים ומשתנות לעיתים רחוקות: רשימות תבניות, הגדרות האפליקציה, תוכן קבוע. שומרים אותן בזיכרון או ב־Redis עם תוקף קצר. נתונים אישיים שמשתנים בכל עריכה בדרך כלל עדיף להגיש מהר מאינדקס מאשר מקאש.

## איך API צריך לחלק רשימות ארוכות לעמודים?

אף פעם לא מחזירים אוסף שלם. מחזירים עמוד של תוצאות ודרך לקבל את הבא. ‏skip ו־limit בסדר להיסטים קטנים; ברשימות ארוכות, pagination לפי סמן על שדה עם אינדקס נשאר מהיר בכל עומק:

```js
// Next page: everything older than the last item the client has
const page = await Resume.find({ userId, updatedAt: { $lt: cursor } })
  .sort({ updatedAt: -1 })
  .limit(20)
```

## איך ולידציה וטיפול מרוכז בשגיאות מצמצמים תקלות בפרודקשן?

מוודאים כל גוף בקשה בכניסה, כך שקלט שגוי נדחה עם 400 ברור במקום להיכשל עמוק בתוך הקוד. אחר כך מעבירים כל שגיאה דרך handler אחד ב־Express שרושם אותה עם הקשר ומחזיר תשובה עקבית. השגיאות מפסיקות להיות מפוזרות, ורואים אילו קורות הכי הרבה ומתקנים אותן.

```js
app.use((err, req, res, next) => {
  logger.error({ err, path: req.path, user: req.user?.id })
  const status = err.status || 500
  res.status(status).json({ error: status === 500 ? "Something went wrong" : err.message })
})
```

## איך שומרים על שחרורים יציבים כשהתנועה גדלה?

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

## שאלות נפוצות

### בכמה ה־API של CV Maker נעשה מהיר יותר?

זמן התגובה הממוצע ירד בכ־60%, בזמן שבסיס המשתמשים גדל מ־200 אלף ליותר ממיליון.

### מה זו בעיית N+1 שאילתות?

שליפת רשימה ואז הרצת שאילתה נוספת לכל פריט. זה מכפיל את הסבבים מול מסד הנתונים; ‏aggregation pipeline או שאילתה אחת עם $in בדרך כלל מחליפים את זה.

### להשתמש ב־skip ו־limit ל־pagination?

ברשימות קצרות כן. ברשימות ארוכות עדיף pagination לפי סמן על שדה עם אינדקס, כמו updatedAt, שנשאר מהיר בכל עומק.

### Node.js מספיק מהיר למיליון משתמשים?

כן. בפועל מסד הנתונים ומבנה השאילתות קובעים את הביצועים הרבה יותר מסביבת הריצה.

### למה לרכז את הטיפול בשגיאות ב־Express?

handler אחד רושם כל שגיאה עם הקשר ומחזיר תשובה עקבית, כך שרואים אילו שגיאות קורות הכי הרבה ומתקנים אותן.

### אתה יכול להגדיל את הבקאנד של האפליקציה שלי?

כן. הגדלתי בקאנד של אפליקציית מובייל ליותר ממיליון משתמשים. ראו את עמוד שירות הבקאנד ב־Node.js.

לשירות הקשור: https://shahid09.is-a.dev/he/hire/nodejs-backend-developer/
