It was just another normal day at work. I pushed a new feature, merged my PR, and everything looked fine. Until…
the support team messaged:
“Hey, users are complaining the dashboard is taking forever to load. Can you check?”
😅 Uh oh. My once-fast API was now sluggish.
That’s when I started my debugging journey.
🔎 Step 1: The First Clue — Measuring the Problem
The first rule of debugging: don’t guess — measure.
I added a simple middleware in my NestJS app to log response times:
// logger.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`${req.method} ${req.originalUrl} - ${duration}ms`);
});
next();
}
}
👉 Boom. The metrics showed some endpoints were taking 800ms+ just to respond. Too slow.
💻 Step 2: Client-Side Payloads
The first bottleneck wasn’t even the backend—it was sending too much data.
Our API was returning entire tables in one response.
Fix → Pagination + Compression
// main.ts
import * as compression from 'compression';
app.use(compression()); // reduce payload size
// users.service.ts
async getUsers(page = 1, limit = 20) {
return this.userRepo.find({
skip: (page - 1) * limit,
take: limit,
});
}
👉 Just like that, the response shrank from 2MB → 200KB. Faster load times already.
🗄 Step 3: The Real Culprit — Database
Next, I checked Postgres queries. Running EXPLAIN ANALYZE
revealed the users lookup by email was scanning the whole table.
Fix → Add an Index
CREATE INDEX idx_users_email ON users(email);
And update the NestJS query:
async getUserByEmail(email: string) {
return this.userRepo.findOne({ where: { email } });
}
👉 Query time dropped from 500ms → 20ms. Huge win.
⚡ Step 4: Redis to the Rescue
Some queries were still slow because the same data was being fetched repeatedly.
Solution? Cache it in Redis.
// users.service.ts
async getUserCached(id: number) {
const key = `user:${id}`;
let user = await this.cache.get(key);
if (user) return { source: 'cache', user };
user = await this.userRepo.findOne({ where: { id } });
if (user) await this.cache.set(key, user, 60); // cache for 1 min
return { source: 'db', user };
}
👉 Second request was now 5ms instead of hitting the DB again.
🔗 Step 5: Slow External APIs
One endpoint was calling a third-party API. Sometimes it just hung for 10+ seconds 😱.
Fix → Add Timeout + Retry
// external.service.ts
import axios from 'axios';
async fetchData() {
try {
const response = await axios.get('https://api.example.com/data', {
timeout: 3000, // 3s timeout
});
return response.data;
} catch (err) {
console.error('API failed:', err.message);
return null; // graceful fallback
}
}
👉 No more stuck requests.
🏗 Step 6: Heavy Workloads
We also had tasks like sending emails and generating reports. They were blocking the API.
Fix → Offload to Background Jobs (BullMQ)
// producer.service.ts
import { Queue } from 'bullmq';
const queue = new Queue('emailQueue');
await queue.add('sendEmail', { to: 'user@test.com' });
// worker.ts
import { Worker } from 'bullmq';
new Worker('emailQueue', async job => {
console.log('Sending email:', job.data);
});
👉 API responds instantly while jobs run in the background.
⚙️ Step 7: Scaling Up
Finally, with everything optimized, we scaled the service horizontally:
pm2 start dist/main.js -i max
👉 Multiple processes → better use of all CPU cores.
✅ The Happy Ending
After all these fixes:
- Endpoints went from 800ms → under 100ms.
- Database CPU load dropped by 70%.
- Users stopped complaining 🎉.
✨ Lessons Learned
Debugging a slow API isn’t about magic. It’s about following a process:
- Measure → find the bottleneck.
- Isolate → confirm where the slowdown is.
- Fix → apply the right optimization.
And most importantly: fix the biggest bottleneck first, then repeat.
👉 Next time your API slows down, don’t panic. Just follow the Measure → Isolate → Fix cycle, and maybe you’ll even enjoy the detective work 😉.
Top comments (0)