Skip to main content

How do you chain several streams together?

Ways to chain streams

There are two main ways:

  1. The classic way, via .pipe()
  2. The modern, safe way, via stream.pipeline()

1. Chaining via .pipe()

.pipe() is the most basic way to "connect" streams.

It passes one stream's (Readable) output into another's (Writable or Transform) input:

javascript
const fs = require('fs'); const zlib = require('zlib'); fs.createReadStream('input.txt') // Readable .pipe(zlib.createGzip()) // Transform .pipe(fs.createWriteStream('output.txt.gz')); // Writable

What happens:

  • createReadStream() reads the file in pieces (chunks),
  • createGzip() compresses them on the fly,
  • createWriteStream() writes to the new file.

A chain of several Transform streams

javascript
const fs = require('fs'); const { Transform } = require('stream'); const zlib = require('zlib'); // 1. Convert the text to uppercase const upper = new Transform({ transform(chunk, encoding, callback) { this.push(chunk.toString().toUpperCase()); callback(); }, }); // 2. Compress the result const gzip = zlib.createGzip(); fs.createReadStream('input.txt') .pipe(upper) // Readable → Transform .pipe(gzip) // Transform → Transform .pipe(fs.createWriteStream('output.txt.gz')); // Transform → Writable

Each .pipe() returns the next stream, so long chains can be built.

2. Chaining via pipeline()

Since Node.js v10, there's a pipeline() function (in stream) that does the same thing, but more safely and reliably.

javascript
const { pipeline } = require('stream'); const fs = require('fs'); const zlib = require('zlib'); pipeline( fs.createReadStream('input.txt'), zlib.createGzip(), fs.createWriteStream('output.txt.gz'), (err) => { if (err) console.error('Stream error:', err); else console.log('The file was processed successfully!'); } );

Advantages of pipeline() over .pipe()

Feature.pipe()pipeline()
Error handlingManualAutomatic
Closing every stream on errorNoYes
A completion callback/promiseNoYes
async/await support (stream/promises)NoYes

3. The async variant (stream/promises)

The modern way, with await (Node.js 15+):

javascript
import { pipeline } from 'stream/promises'; import fs from 'fs'; import zlib from 'zlib'; import { Transform } from 'stream'; const upper = new Transform({ transform(chunk, _, cb) { cb(null, chunk.toString().toUpperCase()); }, }); await pipeline( fs.createReadStream('input.txt'), upper, zlib.createGzip(), fs.createWriteStream('output.txt.gz') ); console.log('Every stream finished successfully');

A generalized stream-chain diagram

javascript
ReadableTransformTransformWritable ↓ ↓ ↓ ↓ Source Filter 1 Filter 2 Destination

Each Transform can:

  • compress,
  • encrypt,
  • filter,
  • parse JSON,
  • format text,
  • log data, and so on.

Key rules when chaining streams

  1. Order matters, data flows left to right.
  2. Every .pipe() returns the next stream, which is what lets you build chains.
  3. Don't forget errors if using .pipe() without pipeline():
javascript
stream.on('error', console.error);
  1. Use pipeline() in production, it closes everything on its own if something fails.

Short Answer

Interview ready
Premium

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