Understanding the event loop
Node.js runs on a single-threaded event loop. Any blocking operation (CPU-heavy tasks, sync code) can freeze your entire server.
// ❌ Blocking code
app.get('/report', (req, res) => {
const data = heavyComputation();
res.json(data);
});
Fix this by offloading work to background jobs or worker threads.
// ✅ Non-blocking approach
const { Worker } = require('worker_threads');
Use clustering for multi-core scaling
Node.js doesn’t automatically use multiple CPU cores. Use clustering (or PM2 cluster mode) to fully utilize your server.
const cluster = require('cluster');
const os = require('os');
if (cluster.isPrimary) {
os.cpus().forEach(() => cluster.fork());
} else {
require('./server');
}
Database query optimization
Slow queries are the #1 performance killer in most apps.
- Add indexes on frequently queried fields
- Avoid N+1 queries
- Use pagination instead of loading everything
// ❌ Bad
const users = await User.find();
// ✅ Better
const users = await User.find().limit(20);
Introduce caching (huge win)
Caching can reduce response time from 500ms to under 50ms.
const redis = require('redis');
const client = redis.createClient();
app.get('/products', async (req, res) => {
const cached = await client.get('products');
if (cached) return res.json(JSON.parse(cached));
const data = await getProducts();
await client.setEx('products', 60, JSON.stringify(data));
res.json(data);
});
Enable gzip compression
Reduce response size and improve load time instantly.
const compression = require('compression');
app.use(compression());
Monitor and profile your app
You can’t optimize what you don’t measure. Use tools like:
- Chrome DevTools (CPU profiling)
- PM2 monitoring dashboard
- APM tools like New Relic or Datadog
Real-world results
- Reduced API latency from 420ms → 90ms
- Handled 3x more concurrent users
- Server costs reduced by ~40%
Key takeaway
Performance optimization isn’t about one trick — it’s about fixing bottlenecks across your entire stack: code, database, and infrastructure.