DEV Community

Cover image for Node.js API Gets Slower Over Time A Real Production Bug and the Permanent Fix
ar abid
ar abid

Posted on

Node.js API Gets Slower Over Time A Real Production Bug and the Permanent Fix

If your Node.js API starts fast but becomes slower after a few hours or days, this post is for you.

This is not a scaling issue.
This is not a server issue.
And restarting the app is not a fix.

This is a real production bug caused by memory leaks and event loop blocking — and most developers ship it without realizing.

Symptoms Developers Actively Search For

You are likely facing this problem if:

  • API response time increases gradually
  • Memory usage keeps growing
  • CPU stays normal but latency spikes
  • Restarting the server “fixes” the issue temporarily
  • No errors appear in logs

This problem quietly kills performance in real-world systems.

The Root Cause (What Actually Breaks Your API)

Node.js runs on a single-threaded event loop.
Two things slowly destroy performance:

1. Memory Leaks

Objects stay in memory because references are never released.

Common sources:

  • Global caches without limits
  • Objects stored by user ID
  • Event listeners added repeatedly
  • Closures holding large data

2. Event Loop Blocking

Heavy synchronous work blocks all requests.

Examples:

  • Large JSON parsing
  • Sync file operations
  • CPU-heavy crypto work
  • Infinite promise creation

Step 1 Detect the Memory Leak (Most Tutorials Skip This)

Run your app with inspection enabled:

node --inspect index.js
Enter fullscreen mode Exit fullscreen mode

Open Chrome and go to:

chrome://inspect
Enter fullscreen mode Exit fullscreen mode

Now:

  1. Take a Heap Snapshot
  2. Wait 10–15 minutes
  3. Take another snapshot
  4. Compare object counts

What You’re Looking For

  • Objects that keep increasing
  • Memory that never drops after GC
  • Retained closures

If memory never stabilizes, you’ve found the leak.

Step 2 The Most Common Real Bug (With Fix)

❌ Bad Code (Seen in Production)

const cache = {};

app.get('/user/:id', async (req, res) => {
  const user = await getUser(req.params.id);
  cache[req.params.id] = user;
  res.json(user);
});
Enter fullscreen mode Exit fullscreen mode

Why This Breaks Your API

  • Cache grows forever
  • No eviction
  • Memory never released

✅ Correct Fix Using LRU Cache

import LRU from 'lru-cache';

const cache = new LRU({
  max: 500,
  ttl: 1000 * 60 * 5
});
Enter fullscreen mode Exit fullscreen mode

Now:

  • Memory is capped
  • Old entries expire
  • Performance stays stable

Step 3 Event Listener Leaks (Very Common)

❌ Problem

process.on('message', handler);
Enter fullscreen mode Exit fullscreen mode

This gets added multiple times and never removed.

✅ Fix

process.once('message', handler);
Enter fullscreen mode Exit fullscreen mode

Or explicitly clean up:

process.removeListener('message', handler);
Enter fullscreen mode Exit fullscreen mode

Step 4 Stop Blocking the Event Loop

Search your code for:

  • readFileSync
  • CPU-heavy loops
  • Large JSON operations

Move heavy work off the main thread.

Worker Thread Example

import { Worker } from 'worker_threads';

new Worker('./heavy-task.js');
Enter fullscreen mode Exit fullscreen mode

This alone can reduce response time by 50%+.

Step 5 Monitor the Right Metrics (Not Just CPU)

CPU usage is misleading.

You should monitor:

  • Heap used
  • Event loop delay
  • Garbage collection time

If event loop delay increases, your API will slow down — guaranteed.

Why Restarting “Fixes” the Problem

Restarting:

  • Clears memory
  • Resets the event loop

But:

  • The bug remains
  • Performance degrades again
  • Users experience downtime

This is why real production systems fix leaks instead of hiding them.

Why This Matters Beyond Code

Performance issues aren’t just technical — they affect user trust.
Whether it’s an API or a shopping platform, slow systems lose users.

That’s why performance-focused platforms and curated marketplaces like reliable ecommerce platforms such as Shopperdot emphasize stability and efficiency at every layer.

Final Thoughts

A Node.js API does not naturally slow down over time.
If it does, something is wrong.

Once you:

  • Control memory
  • Remove event loop blockers
  • Monitor heap growth

Your API can run for weeks without degradation.

This is the difference between “it works” and production-ready software.

Top comments (0)