skip to content
logo
Table of Contents

Scaling Node.js for High Traffic

Handling high traffic isn’t just about adding more servers—it’s about efficiency.
This guide covers load balancing, caching, database tuning, and async processing.


** Load Balancing: Distribute Traffic Efficiently **

A single Node.js instance won’t scale indefinitely. Use NGINX or a reverse proxy to distribute traffic.

NGINX Load Balancer

upstream node_servers {
server 192.168.1.10:3000;
server 192.168.1.11:3000;
}
server {
listen 80;
location / {
proxy_pass http://node_servers;
}
}

Alternatives: HAProxy, AWS ALB, Kubernetes Ingress.


** Cache Everything That Can Be Cached**

Use Redis for Fast Data Access

import { createClient } from "redis";
const redis = createClient();
await redis.connect();
const cacheKey = `user:42`;
const cachedData = await redis.get(cacheKey);
if (!cachedData) {
const user = await db.query("SELECT \* FROM users WHERE id = $1", [42]);
await redis.setEx(cacheKey, 3600, JSON.stringify(user));
}

🔹 What to Cache?
✔ Frequently accessed data (users, sessions, settings)
✔ API responses
✔ Computed values (aggregates, reports)


Optimize Database Queries

Databases are often the bottleneck. Optimize queries before adding more resources.

Use Indexes for Faster Lookups

CREATE INDEX idx_users_email ON users (email);

Connection Pooling for Efficiency

import { Pool } from "pg";
const pool = new Pool({ max: 20 });
const result = await pool.query("SELECT \* FROM users WHERE id = $1", [42]);

❌ Bad: Running a new database connection per request.
✅ Good: Using connection pooling.


Move CPU-Heavy Tasks to Worker Threads

Node.js runs single-threaded, so CPU-intensive tasks slow everything down.
Use worker threads to offload heavy computations.

import { Worker } from "worker_threads";
const worker = new Worker("./worker.js");
worker.on("message", (msg) => console.log(`Worker response: ${msg}`));

In worker.js:

import { parentPort } from "worker_threads";
if (parentPort) {
let sum = 0;
for (let i = 0; i < 1e9; i++) sum += i;
parentPort.postMessage(sum);
}

Alternatives: Redis queues (BullMQ), RabbitMQ for distributed task processing.


Implement API Rate Limiting

Too many requests from a single user?
Rate limiting prevents abuse and protects your app.

import rateLimit from "express-rate-limit";
const limiter = rateLimit({
windowMs: 60 \* 1000, // 1 minute
max: 100, // Max 100 requests per IP
});
app.use(limiter);

Best practice: Use Redis-backed rate limiting for distributed servers.


** Key Takeaways **

Use load balancing to distribute traffic.
Cache responses with Redis to reduce database load.
Optimize database queries and use connection pooling.
Offload CPU-heavy tasks to worker threads.
Implement rate limiting to prevent API abuse.

Scaling is about architecture, not just adding more servers.
Test, profile, and optimize before scaling horizontally.