How to use stream.pipe

If you've been using Node.js for a while, you've definitely run into streams. HTTP connections are streams, open files are streams; stdin, stdout, and stderr are all streams as well. A 'stream' is node's I/O abstraction - if you feel like you still need to understand them better, you can read more about them here.

Streams make for quite a handy abstraction, and there's a lot you can do with them - as an example, let's take a look at stream.pipe(), the method used to take a readable stream and connect it to a writeable stream. Suppose we want to spawn a node child process and pipe our stdout and stdin to its corresponding stdout and stdin.

#!/usr/bin/env node

const child = require('node:child_process');

const myREPL = child.spawn('node');

myREPL.stdout.pipe(process.stdout, { end: false });


process.stdin.pipe(myREPL.stdin, { end: false });

myREPL.stdin.on('end', () => {
  process.stdout.write('REPL stream ended.');

myREPL.once('exit', code => process.exit(code));

There you have it - spawn the Node.js REPL as a child process, and pipe your stdin and stdout to its stdin and stdout. Make sure to listen for the child's 'exit' event, too, or else your program will just hang there when the REPL exits.

Another use for stream.pipe() is file streams. In Node.js, fs.createReadStream() and fs.createWriteStream() are used to create a stream to an open file descriptor. Now let's look at how one might use stream.pipe() to write to a file. You'll probably recognize most of the code:

#!/usr/bin/env node

const child = require('node:child_process'),
    fs = require('node:fs');

const myREPL = child.spawn('node'),
    myFile = fs.createWriteStream('myOutput.txt');

myREPL.stdout.pipe(process.stdout, { end: false });


process.stdin.pipe(myREPL.stdin, { end: false });

myREPL.stdin.on("end", () => {
  process.stdout.write("REPL stream ended.");

myREPL.once('exit', code => process.exit(code));

With those small additions, your stdin and the stdout from your REPL will both be piped to the writeable file stream you opened to 'myOutput.txt'. It's that simple - you can pipe streams to as many places as you want.

Another very important use case for stream.pipe() is with HTTP request and response objects. Here we have the very simplest kind of proxy:

#!/usr/bin/env node

const http = require('node:http');

http.createServer((request, response) => {
  const proxy = http.request(request.url, { hostname: "localhost", port: 9000, headers: request.headers, method: request.method });
  proxy.once('response', proxyResponse => {

http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.write('request successfully proxied to port 9000!' + '\n' + JSON.stringify(req.headers, true, 2));

One could also use stream.pipe() to send incoming requests to a file for logging, or to a child process, or any one of a number of other things.

Hopefully this has shown you the basics of using stream.pipe() to easily pass your data streams around. It's truly a powerful little trick in Node.js, and its uses are yours to explore. Happy coding, and try not to cross your streams!

Scroll to top