Course contentsShow
← Node.js
Lesson 389 of 1,9805. Streams and BackpressureFree lesson

Implementing a Custom Writable Stream

Develop a data destination that efficiently consumes and processes incoming data fragments.

In this lesson, you will learn how to build your own Writable stream to act as a custom destination for data, allowing you to intercept and process incoming chunks exactly how your application requires.

The Core Concept

A Writable stream is essentially a "sink." While a Readable stream is a source of data, a Writable stream is the final destination. When you implement a custom one, you aren't just letting Node.js dump bytes into a file or network socket; you are defining the logic that executes every time a chunk of data arrives.

To create one, you extend the Writable class from the stream module and implement the _write method.

Putting it into Practice

The _write method is the heart of your custom stream. It receives three arguments: the current chunk, the encoding (if it's a string), and a callback function. That callback is vital—it tells Node.js, "I have finished processing this chunk, and you can send me the next one."

const { Writable } = require('stream');

class ConsoleLogger extends Writable {
  _write(chunk, encoding, callback) {
    // Process the data (e.g., convert to uppercase)
    console.log(`Processing: ${chunk.toString().toUpperCase()}`);
    
    // Signal that we are ready for the next chunk
    callback();
  }
}

const myStream = new ConsoleLogger();
myStream.write('hello world');
myStream.end('finished');

Why Manual Control Matters

By defining _write, you gain full control over the consumption of data. If your processing logic involves an asynchronous task—like writing to a database—you simply hold off on calling callback() until that database operation completes. This naturally signals to the upstream source that it should pause sending more data, preventing your application from being overwhelmed by memory-heavy data spikes. You are effectively managing the flow by deciding when to "pull" the next piece of data from the internal buffer.

Key Takeaway: Implementing a custom Writable stream gives you total control over how data is consumed by providing a dedicated _write method that uses a callback to safely manage the flow of information.