67

I have just started using Node.js, and I don't know how to get user input. I am looking for the JavaScript counterpart of the python function input() or the C function gets. Thanks.

Serket
  • 3,785
  • 3
  • 14
  • 45
  • https://nodejs.org/en/knowledge/command-line/how-to-prompt-for-command-line-input/? https://stackoverflow.com/q/51370592/3001761? – jonrsharpe Apr 23 '20 at 18:56
  • See the official docs-https://nodejs.org/en/knowledge/command-line/how-to-prompt-for-command-line-input/ – Shubham Dixit Apr 23 '20 at 19:03

7 Answers7

82

There are 3 options you could use. I will walk you through these examples:

(Option 1) prompt-sync: In my opinion, it is the simpler one. It is a module available on npm and you can refer to the docs for more examples prompt-sync.

npm install prompt-sync
const prompt = require("prompt-sync")({ sigint: true });
const age = prompt("How old are you? ");
console.log(`You are ${age} years old.`);

(Option 2) prompt: It is another module available on npm:

npm install prompt
const prompt = require('prompt');

prompt.start();

prompt.get(['username', 'email'], function (err, result) {
    if (err) { return onErr(err); }
    console.log('Command-line input received:');
    console.log('  Username: ' + result.username);
    console.log('  Email: ' + result.email);
});

function onErr(err) {
    console.log(err);
    return 1;
}

(Option 3) readline: It is a built-in module in Node.js. You only need to run the code below:

const readline = require("readline");
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

rl.question("What is your name ? ", function(name) {
    rl.question("Where do you live ? ", function(country) {
        console.log(`${name}, is a citizen of ${country}`);
        rl.close();
    });
});

rl.on("close", function() {
    console.log("\nBYE BYE !!!");
    process.exit(0);
});
Aryan Beezadhur
  • 4,503
  • 4
  • 21
  • 42
Willian
  • 3,011
  • 1
  • 15
  • 38
45

This can also be done natively with promises. It is also more secure then using outside world npm modules. No longer need to use callback syntax.

Updated answer from @Willian. This will work with async/await syntax and ES6/ES7.

const readline = require('readline');

const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
const prompt = (query) => new Promise((resolve) => rl.question(query, resolve));

// Usage inside aync function do not need closure demo only
(async() => {
  try {
    const name = await prompt("What's your name: ");
    // Can use name for next question if needed
    const lastName = await prompt(`Hello ${name}, what's your last name?: `);
    // Can prompt multiple times
    console.log(name, lastName);
    rl.close();
  } catch (e) {
    console.error("Unable to prompt", e);
  }
})();

// When done reading prompt, exit program 
rl.on('close', () => process.exit(0));
Aryan Beezadhur
  • 4,503
  • 4
  • 21
  • 42
Josh
  • 1,059
  • 10
  • 17
  • 2
    just tested: putting the rl declaration (ine 3) inside the async-function ensures, that it goes out of scopes, no need for your very last line then. – Frank N Sep 11 '22 at 19:29
  • 3
    This is the best answer. – Mr. Polywhirl Sep 14 '22 at 19:14
  • How can I make an outer program wait until I've collected all my input? I'm trying to store it and use it, not just print it. – Mote Zart Oct 13 '22 at 18:37
  • You can use the `readline/promises` module for the async/await syntax. – Dev-Siri Feb 13 '23 at 14:20
  • Note that as of Jan 2023, the readline/promises API is considered level 1 - experimental Non-backward compatible changes or removal may occur in any future release. Use of the feature is not recommended in production environments. – KyleMit ♦ Jan 7 at – Josh Feb 17 '23 at 19:42
5

If you want to use ESM (import instead of require):

import * as readline from 'node:readline/promises';  // This uses the promise-based APIs
import { stdin as input, stdout as output } from 'node:process';

const rl = readline.createInterface({ input, output });

const answer = await rl.question('What do you think of Node.js? ');

console.log(`Thank you for your valuable feedback: ${answer}`);

rl.close();

Source: https://nodejs.org/api/readline.html#readline

Note that this is a a new feature. From the source linked above, it seems like node v17.9.1 or above is required.

nessus_pp
  • 858
  • 1
  • 10
  • 14
  • 2
    Note that as of Jan 2023, the [readline/promises](https://nodejs.org/api/readline.html#promises-api) API is considered [level 1 - experimental](https://nodejs.org/api/documentation.html#stability-index) *Non-backward compatible changes or removal may occur in any future release. Use of the feature is not recommended in production environments.* – KyleMit Jan 07 '23 at 19:14
3

The other solutions here are either async, or use the blocking prompt-sync. I want a blocking solution, but prompt-sync consistently corrupts my terminal.

I found a lovely answer here which offers a good solution.

Create the function:

const prompt = msg => {
  fs.writeSync(1, String(msg));
  let s = '', buf = Buffer.alloc(1);
  while(buf[0] - 10 && buf[0] - 13)
    s += buf, fs.readSync(0, buf, 0, 1, 0);
  return s.slice(1);
};

Use it:

const result = prompt('Input something: ');
console.log('Your input was: ' + result);

Disclaimer: I'm cross-posting my own answer from here.

Edit: see an improved version for Linux here.

aggregate1166877
  • 2,196
  • 23
  • 38
  • 2
    Great solution. Yet it would be much better for users if it was hidden behind a simple Node.js built-in function named perhaps console.read(). Above bit-mangling is not simple to understand nor to remember or type right every time you or somebody needs it. As said in the discussion at https://github.com/nodejs/node/issues/28243 if there is built-in console.log(), why isn't there built-in console.read() ? – Panu Logic Nov 15 '22 at 18:33
  • 1
    @PanuLogic I agree. The Node dev community won't budge on this, though, and I don't get why :/. `console.read()` not being implemented I can understand because it pollutes the `console` namespace, but a global `prompt()` makes sense because we already mimic browsers with things like `setTimeout()`, so why not `prompt()` as well? Or maybe in one of the Node utils built-ins if they're concerned about new globals breaking backwards compatibility – aggregate1166877 Nov 15 '22 at 22:08
  • 1
    I get an error on the readsync. "Error: ESPIPE: invalid seek, read". I got it to work by using the openSync method to open stdin first, as suggested here: https://stackoverflow.com/a/21132342/300213 Also, pass null as the last argument to readSync. Could be a linux thing? – user4815162342 Jan 02 '23 at 20:52
  • @user4815162342 Seems like it - I tried in a GUI-less Ubuntu 18.04 VM now and got the same error, using Node 16.13 – aggregate1166877 Jan 02 '23 at 23:30
  • 1
    @aggregate1166877 To clarify: I fixed ESPIPE by passing null as the last argument to readSync instead of 0. This gave me an ESAGAIN error, which I fixed by getting the fd to pass to readSync with this statement: `let stdin = fs.openSync("/dev/stdin","rs");`. I suspect this is something like what readline does. I will put my complete solution in a different answer. – user4815162342 Jan 04 '23 at 16:02
2

This also works well:

const fs = require('fs');

process.stdin.resume();
process.stdin.setEncoding('utf-8');

let inputString = '';
let currentLine = 0;

process.stdin.on('data', inputStdin => {
    inputString += inputStdin;
});

process.stdin.on('end', _ => {
    inputString = inputString.replace(/\s*$/, '')
        .split('\n')
        .map(str => str.replace(/\s*$/, ''));

    main();
});

function readLine() {
    return inputString[currentLine++];
}

function main() {
    const ws = fs.createWriteStream(process.env.OUTPUT_PATH);

    const n = parseInt(readLine(), 10); // Read and integer like this

    // Read an array like this
    const c = readLine().split(' ').map(cTemp => parseInt(cTemp, 10));

    let result; // result of some calculation as an example

    ws.write(result + "\n");

    ws.end();
}

Here my process.env.OUTPUT_PATH is set, if yours is not, you can use something else.

2

The answer from aggregate1166877 didn't work on linux, causing ESPIPE and ESAGAIN errors without two changes.

This solution fixes those:

    let stdin = fs.openSync("/dev/stdin","rs");

    const read = function(message) {
        fs.writeSync(process.stdout.fd, message + " ");
        let s = '';
        let buf = Buffer.alloc(1);
        fs.readSync(stdin,buf,0,1,null);
        while((buf[0] != 10) && (buf[0] != 13)) {
            s += buf;
            fs.readSync(stdin,buf,0,1,null);
        }
        return s;
    }
user4815162342
  • 2,058
  • 3
  • 18
  • 23
0

It is a form of asynchronous input/output handling in Node.js. We used standard input and output streams to interact with the user or process input and output data. I have used it in hackerank question.

process.stdin.resume();
process.stdin.setEncoding("utf-8");
var stdin_input = "";

process.stdin.on("data", function (input) {
    stdin_input += input; // Reading input from STDIN using `data` event.
});

//the `end `event is used to know when input reading is complete.
process.stdin.on("end", function () { 
   main(stdin_input); //passing a parameter in main function
});


function main(inp){
process.stdout.write(inp);

}
DaveL17
  • 1,673
  • 7
  • 24
  • 38