← thecodex.expert · The Codex Family of Knowledge
The Codex / JavaScript / streams — Streams

streams — Streams

Processing data incrementally in Node.js — Readable, Writable, Transform streams, pipeline, and async iteration.

What it is, in plain English: Streams are Node.js's way of processing data piece by piece, without loading the whole thing into memory. A Readable stream produces data. A Writable stream consumes data. A Transform stream does both — it receives data, transforms it, and outputs the result. pipeline() connects streams together and handles errors and cleanup. Streams implement the async iterable protocol — you can use for await...of.
Where this bites you

Chunk boundaries ignore your data's structure. A file stream hands you 64 KB at a time, so a CSV row is cut in half whenever it straddles two chunks. A Transform that splits each chunk on "\n" on its own produces one wrong row from the first half and one garbage row from the second. Small test files fit in one chunk and never show it.

import { Transform } from "stream";
import { createReadStream } from "fs";

class CSVParser extends Transform {
  constructor() {
    super({ objectMode: true });
    this.headers = null;
  }
  _transform(chunk, enc, cb) {
    const lines = chunk.toString().split("\n").filter(Boolean);   // each chunk on its own
    for (const line of lines) {
      const values = line.split(",");
      if (!this.headers) { this.headers = values; continue; }
      this.push(Object.fromEntries(this.headers.map((h, i) => [h, values[i]])));
    }
    cb();
  }
}

// people.csv: a header + 5,000 rows, every city "Mumbai" (94 KB: two chunks, 64 KB + 30 KB)
let count = 0;
const broken = [];
createReadStream("people.csv").pipe(new CSVParser())
  .on("data", (row) => { count++; if (row.city !== "Mumbai") broken.push(row); })
  .on("end", () => { console.log("rows:", count); console.log("broken:", broken); });
// rows: 5001
// broken: [ { name: 'user3507', age: '27', city: 'Mum' },
//           { name: 'bai', age: undefined, city: undefined } ]

Why streams — memory-efficient data processing

Without streams, to process a large file you load it all into memory. A 10GB log file would need 10GB of RAM. Streams process data in small chunks — the same task uses only kilobytes of memory.

The four stream types

// Readable  — source of data
// Writable  — destination for data
// Transform — reads, transforms, writes (both ends)
// Duplex    — separate read and write (like a TCP socket)

// You use streams constantly without realising it:
process.stdin;                      // Readable
process.stdout;                     // Writable
fs.createReadStream("file.txt");    // Readable
fs.createWriteStream("out.txt");    // Writable
zlib.createGzip();                  // Transform
http.IncomingMessage;               // Readable (req in createServer)
http.ServerResponse;                // Writable (res in createServer)

Reading a stream with async iteration

import { createReadStream } from "fs";
import { createInterface }  from "readline";

// Read a large file line by line (memory-efficient):
const rl = createInterface({
  input:     createReadStream("large.csv"),
  crlfDelay: Infinity,
});

let lineNumber = 0;
for await (const line of rl) {
  lineNumber++;
  if (lineNumber === 1) continue;  // skip header
  processRow(line.split(","));
}
console.log(`Processed ${lineNumber} lines`);

pipeline — the correct way to connect streams

import { createReadStream, createWriteStream } from "fs";
import { createGzip } from "zlib";
import { pipeline }   from "stream/promises";

// Compress a file:
await pipeline(
  createReadStream("big-file.json"),   // read
  createGzip(),                         // compress
  createWriteStream("big-file.json.gz") // write
);
// pipeline: handles backpressure, errors, and cleanup automatically

// ✗ Don't use .pipe() directly — it doesn't handle errors:
// readStream.pipe(gzip).pipe(writeStream);  // if gzip fails, streams don't close!
Try It Yourself

Official Sources

The Codex links only to official documentation and the ECMAScript specification. No third-party tutorials.