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)