stdout
, stdin
, and stderr
in Node.jsstdout
, 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.
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.
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
, and stderr
.
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 process
object 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 programprocess.stdout
(1): The standard output stream, which is a source of output from the programprocess.stderr
(2): The standard error stream, which is used for error messages and diagnostics issued by the programstdin
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.js process.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.
We can stop the running process in our terminal by pressing ctrl + c
.
readline
to create interactive terminal scriptsreadline
is a Node.js module that provides an interface for reading data from a Readable stream (such as process.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.js const 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.js const 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.js const 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.js const 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: ")
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.js process.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.
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.txt SUCCESS: 2 + 2 is 4 SUCCESS 5 + 5 is 10 SUCcESS 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.
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.
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.
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.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.
4 Replies to "Using <code>stdout</code>, <code>stdin</code>, and <code>stderr</code> in Node.js"
Thanks for the explanation. I was not aware how to use it and you teach me the basis.
I am glad you find it helpful
Great article. Thanks. I’m by no means an expert, but re: your comment “It is pretty straightforward to understand why stdin and stdout exist. However, stderr seems pretty odd.” and very much in keeping with your explanation, stdout and stderr are separate channels and may, for example, be separately redirected to two different files. So stderr is sometimes very useful for that purpose, etc.
I came across this while searching about an error that occured while running my react projects. The error occurs while running build command on our project using git bash. Following is the error: > @mo/[email protected] prepare
> npm run build:dist
npm ERR! Cannot read properties of undefined (reading ‘stdin’)
npm ERR! A complete log of this run can be found in: C:\Users\Firmusoft\AppData\
Local\npm-cache\_logs\2023-10-12T04_41_27_328Z-debug-0.log
Does anyone have an idea about this?. I have uninstalled and reinstalled Node and npm and have also removed package-lock.json and cleared npm cache, but still the error persists.
My Node version is 20.2.0 and npm version is 9.6.6 .
Thanks!!