Skip to main content

What is backpressure?

Backpressure is one of the key principles behind streams in Node.js. It explains how the system prevents overload when one stream produces data faster than another can process it.

In plain words

Backpressure is a mechanism for slowing data transfer down in a chain of streams, so that a Writable Stream doesn't "drown" in data when a Readable Stream is too fast.

Picture a garden hose: if the outlet is clogged, pressure builds up, water flows slower. Streams in Node.js work the same way.

Why it matters

Without a backpressure mechanism:

  • a Writable Stream would overflow its internal buffer,
  • the process's memory (RAM) would grow out of control,
  • the Event Loop would freeze under the load.

Node.js handles this automatically when .pipe() or pipeline() is used.

How it works

Every stream has an internal buffer, a memory area that temporarily holds chunks.

  • Writable.write(chunk) returns true if the buffer isn't full yet.
  • It returns false when reading needs to pause until the buffer frees up.

Once the buffer is ready again, the stream fires a drain event, signaling that writing can continue.

An example (without pipe)

javascript
const fs = require('fs'); const readable = fs.createReadStream('bigfile.txt'); const writable = fs.createWriteStream('copy.txt'); readable.on('data', (chunk) => { const canContinue = writable.write(chunk); if (!canContinue) { // The buffer is full, pause reading readable.pause(); } }); writable.on('drain', () => { // The buffer freed up, resume reading readable.resume(); }); readable.on('end', () => writable.end());

Here:

  • .write() returns false when Writable can't keep up;
  • .pause() and .resume() manually control the reading speed;
  • .drain signals when to continue.

An example with pipe (automatic)

If .pipe() is used, backpressure is handled automatically:

javascript
const fs = require('fs'); fs.createReadStream('bigfile.txt') .pipe(fs.createWriteStream('copy.txt'));
  • .pipe() tracks when Writable is overloaded,
  • pauses Readable as needed,
  • resumes it once Writable is ready to write again.

How this looks logically

javascript
ReadableWritable If Writable.write()false Readable.pause() ← the stream stops When Writable fires 'drain' Readable.resume() ← reading resumes

An example via a Transform Stream

The mechanism also works across a chain of several streams:

javascript
const { Transform, pipeline } = require('stream'); const fs = require('fs'); const slowTransform = new Transform({ transform(chunk, _, callback) { setTimeout(() => { this.push(chunk); callback(); }, 100); // artificially slow down processing }, }); pipeline( fs.createReadStream('input.txt'), slowTransform, fs.createWriteStream('output.txt'), (err) => { if (err) console.error('Error:', err); else console.log('Processing finished'); } );

Even if the transformer processes slowly, Node.js automatically regulates the flow, reading never outpaces writing.

The core idea in one sentence

Backpressure is a "smart brake" that prevents buffer overflow, keeping reading and writing speeds in balance.

A quick comparison

BehaviorWithout backpressureWith backpressure
Streams read without stoppingyesno
Memory grows uncontrollablyyesno
Data gets lost or cut offpossibleno
Performance stays stablenoyes

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.