Skip to the solution
MediumLow-Level Design6 min read

What are Node.js Streams and when would you use them?

615 views

The problem

Question presented to candidate: "You need to compress a large file while reading it from disk and writing the result to a new file, without ever holding the whole file in memory. What Node.js concept is built exactly for this?"

What a strong answer should cover:

  • A stream is an abstraction for working with data incrementally, in chunks, rather than loading an entire dataset into memory before processing it β€” the foundational idea behind file I/O, HTTP request/response bodies, and compression, all built on the same stream interfaces.
  • There are four stream types: Readable (a source of data β€” a file read, an HTTP request body), Writable (a destination β€” a file write, an HTTP response), Duplex (both readable and writable, independently β€” a TCP socket), and Transform (a Duplex stream where the writable side's input is processed into the readable side's output β€” compression, encryption, parsing).
  • πŸ“Œ Verified, not just described: a real custom Transform stream (uppercasing each chunk) correctly produced "HELLO WORLD" from two separately-written chunks β€” confirming the transform genuinely processes data incrementally as it flows through, not as an all-at-once operation.
  • Streams exist specifically to solve the problem covered with directly measured proof in the dedicated readFile-vs-createReadStream and blocking-vs-non-blocking questions: bounding memory usage and starting output before an entire input has been fully read, regardless of total data size.
  • .pipe() (and its modern replacement, stream.pipeline(), both covered in their own dedicated questions) connects streams together β€” fs.createReadStream(input).pipe(zlib.createGzip()).pipe(fs.createWriteStream(output)) is the canonical answer to the compression scenario in the prompt: a Readable, through a Transform, into a Writable, with no full-file buffer ever held in memory.
  • A precise answer names that streams are also EventEmitters underneath ('data', 'end', 'error', 'finish' events) β€” the two question topics connect directly, covered in the dedicated EventEmitter question.

Clarifying questions expected:

  • "Is the data source/destination large enough, or streamed from an external source, such that memory-bounding actually matters here?" β€” for a genuinely small, fully-available dataset, streams add complexity without a real benefit.
  • "Does this need a Transform in the middle, or just a direct Readable-to-Writable pipe?" β€” decides how much of the four-type taxonomy is actually relevant.

Code / implementation expected: Yes β€” a real, working custom Transform stream, plus the canonical file-compression pipe chain, is the concrete deliverable here.

  • streams
  • i/o
  • performance
  • data handling

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

6 min read Β· 9 sections

Read the code

JavaScriptA real, working custom Transform stream, plus the canonical Readable-Transform-Writable compression chain
const { Transform } = require("stream");
const fs = require("fs");
const zlib = require("zlib");

// A real custom Transform stream:
const upper = new Transform({
  transform(chunk, enc, cb) {
    this.push(chunk.toString().toUpperCase());
    cb();
  },
});
let out = "";
upper.on("data", (c) => (out += c));
upper.on("end", () => console.log(out)); // HELLO WORLD
upper.write("hello ");
upper.write("world");
upper.end();

// The canonical use case β€” compressing a large file with bounded memory:
fs.createReadStream("input.txt")
  .pipe(zlib.createGzip())
  .pipe(fs.createWriteStream("input.txt.gz"));
// Readable -> Transform -> Writable; the whole file is never in memory at once.

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 82 of 152 in Node.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Node.js questions
Up nextMedium

What is the difference between an ORM, a query builder, and a raw driver?

Continue