Node.js Cheatsheet

Streams

Use this Node.js reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

Stream Types

TypeClassDirectionExample
Readablestream.ReadableSource of datafs.createReadStream, req
Writablestream.WritableSink of datafs.createWriteStream, res
Duplexstream.DuplexBothTCP sockets, net.Socket
Transformstream.TransformRead+Write with mutationzlib.createGzip, crypto.Cipher
PassThroughstream.PassThroughTransform that passes bytes throughtee/spy

Reading Streams

import fs from "node:fs";

const rs = fs.createReadStream("big.txt", {
  encoding:   "utf8",    // omit for Buffer chunks
  highWaterMark: 64 * 1024,  // chunk size hint (bytes)
  start: 0,
  end:   1023,           // inclusive — first 1 KB only
});

// Event-based
rs.on("data",  (chunk) => process.stdout.write(chunk));
rs.on("end",   () => console.log("done"));
rs.on("error", (err) => console.error(err));
rs.on("close", () => { /* fd closed */ });

// Async iterator (recommended — automatic backpressure)
for await (const chunk of rs) {
  process.stdout.write(chunk);
}

// Pause / resume
rs.pause();
rs.resume();
rs.isPaused();     // boolean
rs.readableLength; // bytes in internal buffer

Writing Streams

import fs from "node:fs";

const ws = fs.createWriteStream("out.txt", {
  encoding:      "utf8",
  flags:         "w",         // "a" to append
  highWaterMark: 16384,
  mode:          0o644,
});

// write() returns false when buffer is full
const ok = ws.write("hello\n");
if (!ok) {
  // backpressure — pause the source
  rs.pause();
  ws.once("drain", () => rs.resume());
}

ws.end("final chunk\n");  // flush + close
ws.end();                 // no final data

ws.on("finish", () => console.log("all flushed"));
ws.on("close",  () => console.log("fd closed"));
ws.on("error",  console.error);

ws.writableLength;  // bytes in buffer
ws.writableEnded;   // end() called
ws.writableFinished;// all data flushed

pipe

// Simple pipe (handles backpressure automatically)
readable.pipe(writable);

// Chain transforms
fs.createReadStream("in.gz")
  .pipe(zlib.createGunzip())
  .pipe(fs.createWriteStream("out.txt"));

// Unpipe
readable.unpipe(writable);
readable.unpipe();   // unpipe all

// DANGER: pipe does NOT propagate errors — handle each stage
readable
  .on("error", cleanup)
  .pipe(transform)
  .on("error", cleanup)
  .pipe(writable)
  .on("error", cleanup);

Collect Stream to Buffer / String

// stream/consumers (Node 16+)
import { text, json, buffer, arrayBuffer, blob } from "node:stream/consumers";

const str  = await text(readable);
const obj  = await json(readable);
const buf  = await buffer(readable);

// Manual
async function collect(stream) {
  const chunks = [];
  for await (const chunk of stream) chunks.push(Buffer.from(chunk));
  return Buffer.concat(chunks);
}

Creating Custom Readable Streams

import { Readable } from "node:stream";

// From async generator (simplest)
function range(start, end) {
  return Readable.from(async function* () {
    for (let i = start; i <= end; i++) yield `${i}\n`;
  }());
}

// Class-based
class CounterStream extends Readable {
  constructor(opts) {
    super({ ...opts, objectMode: true });
    this.n = 0;
  }
  _read(size) {
    if (this.n >= 10) {
      this.push(null);  // signal end
    } else {
      this.push(this.n++);
    }
  }
}

// Readable.from — wrap any iterable or async iterable
const letters = Readable.from(["a", "b", "c"]);
const numbers = Readable.from([1, 2, 3], { objectMode: true });

Creating Custom Writable Streams

import { Writable } from "node:stream";

class LogStream extends Writable {
  _write(chunk, encoding, callback) {
    console.log(chunk.toString());
    callback();          // signal done — call with error to abort
  }
  _final(callback) {
    console.log("stream ended");
    callback();
  }
}

Creating Custom Transform Streams

import { Transform } from "node:stream";

class UpperCase extends Transform {
  _transform(chunk, encoding, callback) {
    this.push(chunk.toString().toUpperCase());
    callback();
  }
}

// Inline via object (uses the same Transform import)
const upper = new Transform({
  transform(chunk, enc, cb) {
    cb(null, chunk.toString().toUpperCase());
  },
});

Object Mode Streams

// objectMode: true — chunks can be any JS value, not just string/Buffer
const readable = new Readable({ objectMode: true, read() {} });
readable.push({ id: 1, name: "Alice" });
readable.push({ id: 2, name: "Bob" });
readable.push(null);  // end

for await (const obj of readable) {
  console.log(obj.name);
}

Backpressure Cheat Sheet

// Source must respect writable's capacity
writable.on("drain", () => {
  // buffer cleared — safe to write again
  source.resume();
});

if (!writable.write(data)) {
  source.pause();   // stop producing until drain
}

// With pipeline() or async iterators, backpressure is automatic

Useful Built-in Streams

// Standard I/O
process.stdin    // Readable
process.stdout   // Writable
process.stderr   // Writable

// Compression
import zlib from "node:zlib";
zlib.createGzip()         // Readable → compressed
zlib.createGunzip()       // compressed → original
zlib.createBrotliCompress()
zlib.createBrotliDecompress()
zlib.createDeflate()
zlib.createInflate()

// Crypto
import crypto from "node:crypto";
const iv = crypto.randomBytes(12);        // GCM needs a unique IV per key
crypto.createCipheriv("aes-256-gcm", key, iv)    // Transform — encrypt
crypto.createDecipheriv("aes-256-gcm", key, iv)  // Transform — decrypt
crypto.createHash("sha256")       // Transform — hash computation
crypto.createHmac("sha256", key)  // Transform — HMAC

// Net
import net from "node:net";
net.createConnection(port, host)  // Duplex socket

stream/promises API

import { pipeline, finished } from "node:stream/promises";

// finished — resolves when stream is finished/closed, rejects on error
await finished(writable);

// pipeline — cleans up all streams on error
try {
  await pipeline(src, transform, dest);
} catch (err) {
  console.error("Pipeline failed:", err);
}