How to Run Node.js Readline Functions: A Complete Guide to Interactive CLI Input

You run Node.js readline functions by requiring the readline core module, creating an interface with readline.createInterface() pointing to process.stdin and process.stdout, then listening for 'line' events or using methods like question() to handle user input.

The Node.js readline module provides a powerful way to handle streaming input line-by-line, making it essential for building interactive command-line tools. Whether you're prompting users for configuration details or processing large log files, understanding how to run nodejs readline functions unlocks robust stream processing capabilities. According to the Node.js source code, the implementation resides in lib/readline.js and exposes an Interface class that manages input/output streams, line buffering using StringDecoder, and event emission.

Creating a Readline Interface

Basic Setup with process.stdin

To start using readline functions, you must create an interface instance that binds input and output streams. The readline.createInterface() method accepts an options object specifying input (a readable stream) and output (a writable stream).

const readline = require('readline');

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

Configuration Options

The createInterface method supports several critical options defined in lib/readline.js:

  • crlfDelay: Specifies the maximum delay between \r and \n to treat them as a single newline (useful for Windows-style line endings)
  • terminal: Boolean indicating whether the output should be treated as a TTY (set to false when reading from files)
  • prompt: The prompt string to use (defaults to "> ")

Core Readline Functions and Methods

Handling Line-by-Line Input with Events

The Interface instance emits a 'line' event whenever the input stream receives a newline. This is the fundamental mechanism for processing user input incrementally.

rl.on('line', (line) => {
  console.log(`Received: ${line}`);
  if (line === 'exit') {
    rl.close();
  }
});

rl.on('close', () => {
  console.log('Interface closed');
  process.exit(0);
});

Prompting Users with question()

The question() method provides a convenient way to ask a single question and receive the answer through a callback. According to the source in lib/readline.js, this method writes the query to the output stream, then waits for the next 'line' event.

rl.question('What is your favorite programming language? ', (answer) => {
  console.log(`You chose: ${answer}`);
  rl.close();
});

Writing Output and Prompts

  • prompt(): Redisplays the current prompt without waiting for input
  • write(): Writes arbitrary text to the output stream without affecting the prompt line
rl.write('Loading configuration...\n');
rl.prompt();

Modern Async Patterns

Using Async Iterators (Node.js v10+)

Since Node.js v10, the readline Interface implements [Symbol.asyncIterator], allowing you to process lines using for await...of loops. This pattern, benchmarked in benchmark/readline/readline-iterable.js, provides cleaner syntax for asynchronous input handling.

const rl = require('readline').createInterface({
  input: process.stdin,
  crlfDelay: Infinity
});

(async () => {
  for await (const line of rl) {
    console.log(`> ${line}`);
    if (line === 'quit') break;
  }
})();

Processing File Streams

When reading from files instead of interactive terminals, set terminal: false to disable prompt handling and TTY-specific behaviors. The test file test/pseudo-tty/readline-dumb-tty.js demonstrates handling minimal terminal capabilities.

const fs = require('fs');
const readline = require('readline');

const fileStream = fs.createReadStream('data.txt');
const rl = readline.createInterface({
  input: fileStream,
  output: process.stdout,
  terminal: false
});

rl.on('line', (line) => {
  console.log(`File line: ${line}`);
});

Summary

  • Create an interface using readline.createInterface() with input and output streams, typically process.stdin and process.stdout.
  • Process input by listening for 'line' events or using the question() method for single prompts.
  • Use async iterators (for await...of) for modern, promise-based line processing available in Node.js v10 and later.
  • Handle files by setting terminal: false and passing a file stream to the input option.
  • Clean up by calling rl.close() to remove listeners and restore terminal state.

Frequently Asked Questions

What is the difference between readline and readFile for reading lines?

readline processes streams line-by-line in real-time with minimal memory usage, making it ideal for large files or interactive input. fs.readFile loads the entire file into memory as a buffer or string, which is inefficient for large datasets but simpler for small configuration files.

How do I handle Ctrl+C (SIGINT) in a readline interface?

The readline interface automatically handles Ctrl+C by emitting a 'SIGINT' event if the input is a TTY. Listen for this event to perform cleanup or exit gracefully: rl.on('SIGINT', () => { rl.close(); process.exit(0); });.

Can I use readline with promises instead of callbacks?

Yes. Since Node.js v10, you can use the async iterator protocol (for await...of) to process lines as promises. For the question() method specifically, you can promisify it using util.promisify or wrap it in a Promise constructor to avoid callback-based code.

Why does my readline script hang after processing all lines?

This typically happens because the input stream (like process.stdin) remains open waiting for more data. Always call rl.close() when finished, or ensure the input stream ends (e.g., via EOF/Ctrl+D). For file streams, the interface automatically closes when the file ends, but for stdin, you must explicitly close or exit the process.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →