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)