Spoiler: It wasn’t my server specs.

So there I was, deploying my app β€” can’t drop the real name, NDA things πŸ˜… β€” feeling pretty good about myself. Everything worked perfectly in development, right?

Wrong.

The moment real users started hitting my API, everything went to hell. Response times went from 200ms to 5+ seconds.

My database was screaming.

Users were complaining.

I was panicking.

“But it works fine on my machine!” πŸ€¦β€β™‚️

Turns out, I was making the classic N+1 mistake. And honestly? I had no idea what that even meant until I spent 3 days debugging this mess.

What the Heck is N+1?

Picture this: You’re fetching 100 courses from your database.

Sounds simple, right?

What I was doing (the wrong way):

// ❌ This looks innocent but it's a performance killer
const courses = await Course.find({}); // 1 query

for (let course of courses) {
    const creator = await User.findById(course.createdBy); // 100 more queries!
    course.creator = creator;
}

Total queries: 101 πŸ˜±

That’s 101 round trips to your database.

Each one takes time.

Multiply that by concurrent users, and boom – your server is toast.

Here’s what my server logs looked like:

Query 1: Find courses (200ms)
Query 2: Find user for course 1 (50ms)
Query 3: Find user for course 2 (50ms)
Query 4: Find user for course 3 (50ms)
... (you get the idea)

Total time: 5.2 seconds for what should have been a 200ms request.

What I should have done:

// βœ… One query with a join - much better!
const courses = await Course.find({})
    .populate('createdBy', 'firstName lastName email'); // Just 1 query!

Total queries: 1 πŸŽ‰

The 3 Fixes You Can Implement Today

1. Use Populate (Mongoose)

// Instead of this mess:
const courses = await Course.find({});
for (let course of courses) {
    course.creator = await User.findById(course.createdBy);
}

// Do this:
const courses = await Course.find({})
    .populate('createdBy', 'firstName lastName email');

2. Add Database Indexes

// In your model file:
courseSchema.index({ createdBy: 1 }); // Index foreign keys
courseSchema.index({ status: 1, createdAt: -1 }); // Compound indexes

3. Use Aggregation for Complex Queries

// When you need multiple joins:
const result = await Course.aggregate([
    {
        $lookup: {
            from: 'users',
            localField: 'createdBy',
            foreignField: '_id',
            as: 'creator'
        }
    }
]);

The Real Impact

After fixing this, my app went from handling 50 concurrent users to 500+.

Same server, same codebase, just better queries.

Performance improvement: 96% faster

User capacity: 10x increase

My sanity: Restored πŸ˜…

What’s Your Experience?

Have you run into the N+1 problem?

What was your “oh shit” moment when you realized your queries were the bottleneck?

Drop a comment below – I’d love to hear your war stories!


If you want to understand different loop patterns in JavaScript, check out my earlier post:
πŸ‘‰ Confused About JavaScript Loops? We Can Help.

And if you want a deeper dive into the N+1 issue, this article is a great read:
πŸ‘‰ What Is the N+1 Problem β€” JavaScript Edition (Accreditly)