How to Synchronously Read User Input in Node.js Without readline
This guide explains how to capture user input synchronously in Node.js by accessing the standard input file descriptor directly, compares it with the asynchronous readline approach, and shows practical code examples for different buffer sizes and platform considerations.
Node.js typically uses the built‑in readline module to read user input asynchronously. The standard example creates an interface with process.stdin and process.stdout, asks a question, logs the answer, and closes the interface.
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question('What\'s your name? ', () => {
console.log(`Your name: ${name}`);
rl.close();
});While this works, the same result can be achieved synchronously by using the file‑descriptor (fd) of the standard input stream. The fs module provides low‑level functions that operate on fds.
First, the article demonstrates basic fd usage by opening a regular file, writing a string, and reading it back.
const fs = require('fs');
// Get file descriptor
const fd = fs.openSync('1.txt', 'w+'); // read & write
console.log('fd', fd);
// Write some data
const writeBytes = fs.writeSync(fd, 'Hello world');
console.log('writed', writeBytes, 'bytes');
// Read into buffer
const buffer = Buffer.alloc(writeBytes);
const readBytes = fs.readSync(fd, buffer, 0, writeBytes, 0);
console.log('read', readBytes, 'bytes');
console.log('text [%s]', buffer.toString());Because standard input is also a file, the same approach can be used to read user input synchronously. The following example opens /dev/stdin (the Unix representation of stdin) and reads a fixed‑size buffer.
const fs = require('fs');
const fd = fs.openSync('/dev/stdin', 'rs');
const BUFSIZE = 256;
const buf = Buffer.alloc(BUFSIZE);
fs.writeSync(process.stdout.fd, 'input> ');
const bytesRead = fs.readSync(fd, buf, 0, BUFSIZE);
console.log('length [%s], data: [%s]', bytesRead, buf.toString());The output shows the prompt and the entered text, e.g., input> Alan followed by length [5], data: [Alan].
Why not use fs.readFileSync ? readFileSync reads an entire file until EOF, which for an interactive stdin would require the user to manually signal EOF (Ctrl‑D), making it unfriendly. It is suitable only when the whole input is known to end, such as in pipe scenarios.
For piping, a simple script can read from stdin using fs.readFileSync:
// pipe.js
const fs = require('fs');
const text = fs.readFileSync('/dev/stdin');
console.log(text.toString());Running the script with a Unix pipe feeds the output of another command into the Node.js process.
Windows note: Windows does not provide /dev/stdin; instead, developers can use process.stdin.fd directly with fs.readSync.
The buffer size can be adjusted. Setting it to 1 reads one character at a time, similar to C's getc:
const fs = require('fs');
const BUFSIZE = 1;
const buf = Buffer.alloc(BUFSIZE);
const fd = fs.openSync('/dev/stdin', 'rs');
fs.writeSync(process.stdout.fd, 'input> ');
let bytesRead = 0;
do {
bytesRead = fs.readSync(fd, buf, 0, BUFSIZE);
console.log('data: [%s]', buf.toString());
} while (bytesRead);Increasing the buffer size reads larger chunks, which is more efficient for big files. For very large inputs, using streams (e.g., fs.createReadStream) is recommended to avoid exhausting memory.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Node Underground
No language is immortal—Node.js isn’t either—but thoughtful reflection is priceless. This underground community for Node.js enthusiasts was started by Taobao’s Front‑End Team (FED) to share our original insights and viewpoints from working with Node.js. Follow us. BTW, we’re hiring.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
