Using stdout, stdin, and stderr in Node.js - LogRocket Blog (2023)

stdout, stdin, and stderr are standard streams, which interconnect input and output communication channels between a program and its environment when the program executes.

Streams generally mean the flow of data. You can think of streams as a conveyor belt in a factory connected to different machines (programs in our case). Different machines can be arranged, directed, and connected with a belt (pipe) in a way to produce a particular result.

Just as we can have physical I/O devices connected (input via mouse, output via monitor), standard streams abstract this, giving us the power of composability in our code.

Using stdout, stdin, and stderr in Node.js - LogRocket Blog (1)

Just like the way we can compose powerful Linux commands from small commands, we can make use of Node.js standard streams to achieve the same in Node.js.

Node.js stdin, stdout, and stdin

When we run a Node.js program, a process is started for that program execution.

The GNU documentation defines a process as “the primitive units for allocation of system resources. Each process has its own address space and (usually) one thread of control. A process executes a program; you can have multiple processes executing the same program, but each process has its own copy of the program within its own address space and executes it independently of the other copies.”

Every process is initialized with three open file descriptors called stdin,stdout, andstderr.

Those three file descriptors are collectively called the standard streams.

A set of the three standard streams is started for a process and we can access them via the processobject in Node.js.

The standards streams are treated as if there are files. An easy way to access any file is by using the unique file descriptor associated with it. In the case of these standards streams, there are unique values assigned to each of them.

  • process.stdin(0): The standard input stream, which is a source of input for the program
  • process.stdout(1): Thestandard output stream, which is a source of output from the program
  • process.stderr(2): Thestandard errorstream, which is used for error messages and diagnostics issued by the program

Simple use of stdin and stdout

Let’s write a simple application that receives data via the terminal and prints a processed output into the terminal.

We’d create a JavaScript file (index.js) and write the code below:

 // index.jsprocess.stdin.on("data", data => { data = data.toString().toUpperCase() process.stdout.write(data + "\n")})

Running the above program creates an event listener to listen for data input, processes the input, and prints the output to the terminal.

Using stdout, stdin, and stderr in Node.js - LogRocket Blog (4)

We can stop the running process in our terminal by pressing ctrl + c.

Making use of readline to create interactive terminal scripts

readline is a Node.js module that provides an interface for reading data from aReadable stream (such asprocess.stdin) one line at a time.

First, we would create a new JavaScript file called index.js, import the readline module into our program, then create a function, ask, that receives a string as an argument and creates a prompt with the string in our terminal:

// index.jsconst readline = require("readline")function ask(question) { // asks a question and expect an answer}

Then we will create an interface using readline that connects stdin to stdout:

// index.jsconst readline = require("readline")const rl = readline.createInterface({ input: process.stdin, output: process.stdout,})function ask(question) { // asks a question and expectings an answer}

We will complete the ask function to expect answers and repeat the whole process recursively:

 // index.jsconst readline = require("readline")const rl = readline.createInterface({ input: process.stdin, output: process.stdout,})function ask(question) { rl.question(question, (answer) => { rl.write(`The answer received: ${answer}\n`) ask(question) })}ask("What is your name: ")

Running the above program would create a terminal interface that keeps looping until we end the Node.js process by pressing ctrl + c in the terminal.

The ctrl + c sends a signal to our running Node.js program called SIGKILL, which tells Node.js to stop our program execution. We can also, programmatically, inform Node.js to stop executing our application by calling process.exit(exitCode).

So we will update our ask function to check if the answer from input “q.” If the input is “q” then it should exit the application:

// index.jsconst readline = require("readline")const rl = readline.createInterface({ input: process.stdin, output: process.stdout,})function ask(question) { rl.question(question, (answer) => { if(answer === "q") { process.exit(1) } rl.write(`The answer received: ${answer}\n`) ask(question) })}ask("What is your name: ") 

What is stderr?

When we write applications or programs, errors may occur due to so many reasons. stderr is the default file descriptor where a process can write error messages.

Consider this code below:

// index.jsprocess.stderr.write("error! some error occurred\n")

Running this application with node index.js would write the error message to our terminal similarly to how stdout would output it.

It is pretty straightforward to understand why stdin and stdout exist. However, stderr seems pretty odd.

More great articles from LogRocket:

  • Don't miss a moment with The Replay, a curated newsletter from LogRocket
  • Learn how LogRocket's Galileo cuts through the noise to proactively resolve issues in your app
  • Use React's useEffect to optimize your application's performance
  • Switch between multiple versions of Node
  • Discover how to animate your React app with AnimXYZ
  • Explore Tauri, a new framework for building binaries
  • Compare NestJS vs. Express.js

In the UNIX/Linux-based ecosystem, there was a period where stderr did not exist. All outputs of UNIX commands could be piped via stdout, including both expected results of the command and errors or diagnostic messages.

This is not considered a best practice as the error can also pass through the pipe which the command attached at the end of the pipe may not understand.

Hence, stderr was created to direct error or diagnostic messages via a different file descriptor, which is 2.

N.B., in Linux, when you pipe commands together, only the expecting result is piped together. The error or diagnostic error message is piped via the stderr file descriptor and is by default printed to the terminal.

Let us play around with this by writing two Node.js programs called logger.js and printer.js.

The logger.js is mocking a logging program, but in our case, the logs are predefined already.

Then the printer.js would read data from the stdin and write them to a file.

First, we will create the logger.js below:

const logObject = [ { type: "normal", message: "SUCCESS: 2 + 2 is 4" }, { type: "normal", message: "SUCCESS 5 + 5 is 10" }, { type: "error", message: "ERROR! 3 + 3 is not 4" }, { type: "normal", message: "SUCESS 10 - 4 is 6" }]function logger() { logObject.forEach(log => { setTimeout(() => { if (log.type === "normal") process.stdout.write(log.message) else process.stderr.write(log.message + '\n') }, 500) })}logger()

Next, we will create another Node.js file that creates or opens a text file, logs.txt, read inputs provided by stdout, and writes them to a file:

const fs = require("fs")fs.open("./logs.txt", "w", (err, fd) => { if (err) throw Error(err.message) process.stdin.on("data", data => { fs.write(fd, data.toString() + "\n", (err) => { if (err) throw Error(err.message) }) })})

To run this application, we can pipe these two programs in our terminal by running:

$ node logger.js | node printer.js

N.B., if you are running the above command with Git Bash in Windows, you may come across the error stdout is not a tty. This is likely an issue with Git Bash. You can run the command using Window Powershell or make the script executable by including a shebang (#!/bin/env node) at the top of the file and running the command above as ./logger.js | ./printer.js.

After execution, we can confirm that only the success logs that passed by stdout made it to the logs.txt:

// logs.txtSUCCESS: 2 + 2 is 4SUCCESS 5 + 5 is 10SUCcESS 10 - 4 is 6

And that the error logs were printed to the terminal. This is the default behavior of stderr but we can also change this through redirection and pipelines.

Wrapping up

Now we understand what the standard steams are and how we can make use of them in our Node.js application. We also know how standard streams can help us to build simple programs that can be channeled to formulate more complex programs.

For instance, printer.js doesn’t necessarily need to be aware of what logger.js does. All printer.js do is receive data from a stdout and write the date to a file.

printer.js can be reused and composed with other programs, even with Linux commands, provided they share the same execution environment.

200’s only Using stdout, stdin, and stderr in Node.js - LogRocket Blog (5) Monitor failed and slow network requests in production

Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third party services are successful, try LogRocket. https://logrocket.com/signup/

LogRocket is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.

LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. Start monitoring for free.

Top Articles
Latest Posts
Article information

Author: Frankie Dare

Last Updated: 11/24/2022

Views: 5727

Rating: 4.2 / 5 (73 voted)

Reviews: 88% of readers found this page helpful

Author information

Name: Frankie Dare

Birthday: 2000-01-27

Address: Suite 313 45115 Caridad Freeway, Port Barabaraville, MS 66713

Phone: +3769542039359

Job: Sales Manager

Hobby: Baton twirling, Stand-up comedy, Leather crafting, Rugby, tabletop games, Jigsaw puzzles, Air sports

Introduction: My name is Frankie Dare, I am a funny, beautiful, proud, fair, pleasant, cheerful, enthusiastic person who loves writing and wants to share my knowledge and understanding with you.