Lesson 02-Common Node.js Modules

Module Module

In Node.js, module loading typically involves three steps: path analysis, file location, and compilation execution.

Module loading prioritizes the following order:

  • System Cache: After a module is executed, it is cached. The system first checks the cache for the module.
  • System Modules: Native modules have the next highest priority after the cache. Some core modules are compiled into binaries, skipping path analysis and file location, and are loaded directly into memory. System modules are defined in the lib directory of the Node.js source code.
  • File Modules: Modules starting with ., .., or / are loaded first. If no file extension is provided, Node.js attempts to append .js, .json, and .node extensions in that order. For performance optimization, it’s recommended to include the file extension for .json and .node files, as Node.js uses synchronous blocking to check file existence.
  • Directory as Module: If a file module is not found but a directory is, Node.js treats the directory as a package per CommonJS specifications. It looks for a package.json file in the project root and uses the main field (e.g., "main": "lib/hello.js") to locate the entry file. If not found, it throws an error: Error: Cannot find module 'lib/hello.js'.
  • node_modules Directory: If neither system nor file modules are found, Node.js searches the node_modules directory in the parent directory, continuing up to the system root.

const exports = module.exports;

Node Module Loading Order

For relative modules, the current script’s path serves as the base path. For example, in a script a.ts with let x = require("./b"), TypeScript searches in this order:

  1. Check if b.ts, b.tsx, or b.d.ts exists in the current directory.
  2. Check if a subdirectory b exists, and if it contains a package.json file with a types field specifying an entry file. If so, load that file.
  3. Check if subdirectory b contains index.ts, index.tsx, or index.d.ts.

For non-relative modules, Node.js starts from the current script’s path and searches upward for a node_modules directory. For example, in a.js with let x = require("b"), TypeScript searches in this order:

  1. Check if b.ts, b.tsx, or b.d.ts exists in the current directory’s node_modules.
  2. Check if node_modules contains a package.json file with a types field specifying an entry file. If so, load that file.
  3. Check if node_modules contains an @types subdirectory with b.d.ts.
  4. Check if node_modules contains a subdirectory b with index.ts, index.tsx, or index.d.ts.
  5. Move to the parent directory and repeat the above steps until found.

module.exports and exports

Execution

(function (exports, require, module, __filename, __dirname) { // Wrapper head
  console.log('hello world!') // Original file
}); // Wrapper tail

exports

  • exports is a property of module, defaulting to an empty object.
  • Requiring a module returns its exports property.
  • exports.xxx exports an object with multiple properties.
  • module.exports = xxx exports a single object.

Usage

// module-2.js
exports.method = function () {
  return 'Hello';
};

exports.method2 = function () {
  return 'Hello again';
};

// module-1.js
const module2 = require('./module-2');
console.log(module2.method()); // Hello
console.log(module2.method2()); // Hello again

Events Module

The Events module (EventEmitter) is a critical component in Node.js, implementing the publish/subscribe pattern. It is foundational to many Node.js modules, such as Net, HTTP, FS, and Stream.

const EventEmitter = require('events').EventEmitter;
const emitter = new EventEmitter();
// Register a listener
emitter.on('wake-up', function (time) {
  console.log(`Starting at ${time} in the morning, keep pushing forward!`);
});
// Trigger the event
emitter.emit('wake-up', '6:00');

EventEmitter in the Stream Module

const EventEmitter = require('events');
const util = require('util');

function Stream() {
  EventEmitter.call(this);
}
util.inherits(Stream, EventEmitter);

EventEmitter in the Net Module

const EventEmitter = require('events');
const util = require('util');

function Server(options, connectionListener) {
  if (!(this instanceof Server))
    return new Server(options, connectionListener);
  EventEmitter.call(this);
  // ...
}
util.inherits(Server, EventEmitter);

Using EventEmitter to Handle Avalanche Issues in High Concurrency

The once method ensures a listener is executed only once and then removed. By queuing callbacks for identical requests in the event queue, you can prevent redundant queries for the same file or database, reducing overhead.

const events = require('events');
const emitter = new events.EventEmitter();
const fs = require('fs');
const status = {};

const select = function (file, filename, cb) {
  emitter.once(file, cb);
  if (status[file] === undefined) {
    status[file] = 'ready'; // Set default value
  }
  if (status[file] === 'ready') {
    status[file] = 'pending';
    fs.readFile(file, function (err, result) {
      console.log(filename);
      emitter.emit(file, err, result.toString());
      status[file] = 'ready';
      setTimeout(function () {
        delete status[file];
      }, 1000);
    });
  }
};

for (let i = 1; i <= 11; i++) {
  if (i % 2 === 0) {
    select(`/tmp/a.txt`, 'File a', function (err, result) {
      console.log('err: ', err, 'result: ', result);
    });
  } else {
    select(`/tmp/b.txt`, 'File b', function (err, result) {
      console.log('err: ', err, 'result: ', result);
    });
  }
}

Despite multiple file query requests, the fs module only executes two queries (for files a and b). The once listener prevents redundant queries for identical requests, addressing concurrency issues.

Always register an error event listener to handle errors:

const events = require('events');
const emitter = new events.EventEmitter();

emitter.on('error', function (err) {
  console.error(err);
});

emitter.emit('error', new Error('This is an error'));
console.log('test');

Crypto Module

The Crypto module, built on C/C++ with OpenSSL, provides JavaScript interfaces for hashing, HMAC, encryption, decryption, signing, and verification.

Data Encryption

  • crypto.createCipher(algorithm, pwd): Creates a cipher object with the specified algorithm and password.
  • crypto.createCipheriv(algorithm, pwd, iv): Creates a cipher object with the specified algorithm, password, and initialization vector.
function cipher(str) {
  try {
    const crypto = require('crypto');
    const cipher = crypto.createCipheriv('des-ecb', '12345678', '');
    /**
     * update method
     * 1st param: Data to encrypt
     * 2nd param: Input data format ('utf8', 'ascii', 'latin1')
     * 3rd param: Output format ('latin1', 'base64', 'hex'). Returns Buffer if unspecified
     */
    let encrypted = cipher.update(str, 'utf8', 'hex');
    /**
     * final method: Returns remaining encrypted content
     * Param: Output format ('latin1', 'base64', 'hex'). Returns Buffer if unspecified
     */
    encrypted += cipher.final('hex');
    return encrypted;
  } catch (e) {
    console.log('Encryption failed');
    return e.message || e;
  }
}

cipher('hello world !!!'); // 81c66a1d39d302205c55f0afac95c06bc985155d4ddb751c

Data Decryption

  • crypto.createDecipher(algorithm, pwd): Creates a decipher object with the specified algorithm and password.
  • crypto.createDecipheriv(algorithm, pwd, iv): Creates a decipher object with the specified algorithm, password, and initialization vector.
function decipher(encrypted) {
  try {
    const crypto = require('crypto');
    const decipher = crypto.createDecipheriv('des-ecb', '12345678', '');
    let decrypted = decipher.update(encrypted, 'hex', 'utf8');
    decrypted += decipher.final('utf8');
    return decrypted;
  } catch (e) {
    console.log('Decryption failed');
    return e.message || e;
  }
}

decipher('81c66a1d39d302205c55f0afac95c06bc985155d4ddb751c'); // hello world !!!

sha1, md5, sha256, sha512 Encryption

const crypto = require('crypto');

const md5 = str => {
  return crypto.createHash('md5').update(str, 'utf8').digest('hex');
};

const sha256 = str => {
  return crypto.createHash('sha256').update(str, 'utf8').digest('hex');
};

const sha512 = str => {
  return crypto.createHash('sha512').update(str, 'utf8').digest('hex');
};

// Default output: 32-bit lowercase
// 25f9e794323b453885f5181f1b624d0b
console.log(md5('123456789')); 
// Convert to 32-bit uppercase
// 25F9E794323B453885F5181F1B624D0B
console.log(sha256('123456789').toUpperCase());
console.log(sha512('123456789').toUpperCase());

Buffer Module

The Buffer module is used to read or manipulate binary data streams.

Creating a Buffer (Three Methods)

const b1 = Buffer.from('10', 'utf8');
const bAlloc1 = Buffer.alloc(10); // Creates a 10-byte buffer
const bAllocUnsafe1 = Buffer.allocUnsafe(10);

Buffer Character Encoding

// Supported encodings: 'ascii', 'utf8', 'utf16le', 'base64', 'ucs2', 'latin1', 'binary', 'hex'
const buf = Buffer.from('hello world', 'ascii');
console.log(buf.toString('hex')); // 68656c6c6f20776f726c64

Converting Between Strings and Buffers

const buf = Buffer.from('Node.js Tech Stack', 'utf-8'); // String to Buffer
const str = buf.toString('utf-8', 0, 9); // Buffer to String

Buffer Memory Mechanism

Buffers are allocated in JavaScript using out-of-heap memory:

  1. Upon initialization, an 8KB memory space is allocated (as seen in buffer.js source code).
  2. Memory requests are classified as small or large Buffer objects.
  3. For small Buffers:
  • If the slab space is sufficient, it uses the remaining space and updates the allocation state (increasing the offset).
  • If insufficient, a new slab is created for allocation.
  1. For large Buffers, the createUnsafeBuffer(size) function is used directly.
  2. Memory allocation occurs at the C++ level, while management is handled in JavaScript, allowing V8’s garbage collector to reclaim memory.

Buffer vs. Cache

  • Buffer: Temporary storage for binary stream data, used to accumulate data before processing (e.g., video players buffer stream data before saving to disk).
  • Cache: A persistent intermediate layer for storing frequently accessed data to improve access speed (e.g., using Memory or Redis to cache data from disks or third-party APIs).

Console Logging Module

The node:console module provides a simple debugging console similar to the JavaScript console in web browsers. The Console class includes methods like console.log(), console.error(), and console.warn().

Logger Module Implementation

  • Initialize a Logger object.
  • Validate parameters to ensure the object is a Logger instance and a writable stream.
  • Define _stdout, _stderr, and other properties for the Logger.
  • Bind prototype methods to the Logger instance.
  • Implement log, error, warn, trace, clear, and other methods.
const util = require('util');

/**
 * Initialize Logger object
 * @param {*} stdout
 * @param {*} stderr
 */
function Logger(stdout, stderr) {
  // Step 1: Check if the current object is a Logger instance
  if (!(this instanceof Logger)) {
    return new Logger(stdout, stderr);
  }
  // Check if stdout is a writable stream
  if (!stdout || !(stdout.write instanceof Function)) {
    throw new Error('Logger expects a writable stream instance');
  }
  // Use stdout as stderr if not specified
  if (!stderr) {
    stderr = stdout;
  }
  // Define object properties
  const props = {
    writable: true, // Can the property be modified? Default: true
    enumerable: false, // Can the property be enumerated in for-in loops? Default: true
    configurable: false // Can the property be deleted or redefined? Default: true
  };
  // Define _stdout property
  Object.defineProperty(this, '_stdout', Object.assign(props, {
    value: stdout,
  }));
  // Define _stderr property
  Object.defineProperty(this, '_stderr', Object.assign(props, {
    value: stderr,
  }));
  // Define _times property
  Object.defineProperty(this, '_times', Object.assign(props, {
    value: new Map(),
  }));
  // Bind prototype methods to the Logger instance
  const keys = Object.keys(Logger.prototype);
  for (let k in keys) {
    this[keys[k]] = this[keys[k]].bind(this);
  }
}

// Define log method
Logger.prototype.log = function () {
  this._stdout.write(util.format.apply(this, arguments) + '\n');
};
Logger.prototype.info = Logger.prototype.log;

// Define warn method
Logger.prototype.warn = function () {
  this._stderr.write(util.format.apply(this, arguments) + `\n`);
};
Logger.prototype.error = Logger.prototype.warn;

// Return current call stack information
Logger.prototype.trace = function trace(...args) {
  const err = {
    name: 'Trace',
    message: util.format.apply(null, args)
  };
  // V8 Stack Trace API: https://github.com/v8/v8/wiki/Stack-Trace-API
  Error.captureStackTrace(err, trace);
  this.error(err.stack);
};

// Clear console information
Logger.prototype.clear = function () {
  if (this._stdout.isTTY) {
    const { cursorTo, clearScreenDown } = require('readline');
    cursorTo(this._stdout, 0, 0); // Move cursor to specified position
    clearScreenDown(this._stdout); // Clear from cursor position downward
  }
};

// Output an object directly
Logger.prototype.dir = function (object, options) {
  options = Object.assign({ customInspect: false }, options);
  /**
   * util.inspect(object, [showHidden], [depth], [colors]) converts objects to strings for debugging.
   * showHidden: If true, includes hidden properties.
   * depth: Specifies max recursion depth. Default: 3. Null for unlimited.
   * colors: If true, outputs with ANSI color coding for better terminal display.
   */
  this._stdout.write(util.inspect(object, options) + '\n');
};

// Start timer
Logger.prototype.time = function (label) {
  // process.hrtime() returns high-resolution time as [seconds, nanoseconds]
  this._times.set(label, process.hrtime());
};

// End timer
Logger.prototype.timeEnd = function (label) {
  const time = this._times.get(label);
  if (!time) {
    process.emitWarning(`No such label '${label}' for console.timeEnd()`);
    return;
  }
  const duration = process.hrtime(time);
  const ms = duration[0] * 1000 + duration[1] / 1e6; // 1e6 = 1,000,000
  this.log('%s: %sms', label, ms.toFixed(3));
  this._times.delete(label);
};

module.exports = new Logger(process.stdout, process.stderr);
module.exports.Logger = Logger;

Net Network Module

Network Models

  • OSI Seven-Layer Model: Application, Presentation, Session, Transport, Network, Data Link, Physical.
  • TCP/IP Five-Layer Model: Application, Transport, Network, Data Link, Physical.

TCP Protocol

Three characteristics: Connection-oriented, Byte stream-oriented, Reliable.

Building a TCP Service

// server.js
const net = require('net');
const server = net.createServer(function (socket) {
  // New connection
  socket.on('data', function (data) {
    socket.write('Hello');
  });
  socket.on('end', function () {
    console.log('Connection closed');
  });
  socket.write('Welcome to the "Node.js In-Depth" example:\n');
});
server.listen(8124, function () {
  console.log('Server bound');
});

// client.js
const net = require('net');
const client = net.connect({ port: 8124 }, function () {
  console.log('Client connected');
  client.write('world!\r\n');
});
client.on('data', function (data) {
  console.log(data.toString());
  client.end();
});
client.on('end', function () {
  console.log('Client disconnected');
});

Building a UDP Service

// server.js
const dgram = require('dgram');
const server = dgram.createSocket('udp4');
server.on('message', function (msg, rinfo) {
  console.log(`Server got: ${msg} from ${rinfo.address}:${rinfo.port}`);
});
server.on('listening', function () {
  const address = server.address();
  console.log(`Server listening ${address.address}:${address.port}`);
});
server.bind(41234);

// client.js
const dgram = require('dgram');
const message = Buffer.from('Node.js In-Depth');
const client = dgram.createSocket('udp4');
client.send(message, 0, message.length, 41234, 'localhost', function (err, bytes) {
  client.close();
});

// Output
// $ node server.js
// server listening 0.0.0.0:41234
// server got: Node.js In-Depth from 127.0.0.1:58682

Building an HTTP Service

const http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');

Building a WebSocket Service

const http = require('http');
const crypto = require('crypto');

function WebSocket(url) {
  // Pseudo-code: Parse ws://127.0.0.1:12010/updates for request
  this.options = parseUrl(url);
  this.connect();
}

WebSocket.prototype.onopen = function () {
  // TODO
};

WebSocket.prototype.setSocket = function (socket) {
  this.socket = socket;
};

WebSocket.prototype.connect = function () {
  const that = this;
  const key = Buffer.from(this.options.protocolVersion + '-' + Date.now()).toString('base64');
  const shasum = crypto.createHash('sha1');
  const expected = shasum.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11').digest('base64');
  const options = {
    port: this.options.port, // 12010
    host: this.options.hostname, // 127.0.0.1
    headers: {
      'Connection': 'Upgrade',
      'Upgrade': 'websocket',
      'Sec-WebSocket-Version': this.options.protocolVersion,
      'Sec-WebSocket-Key': key
    }
  };
  const req = http.request(options);
  req.end();
  req.on('upgrade', function (res, socket, upgradeHead) {
    // Connection successful
    that.setSocket(socket);
    that.onopen();
  });
};

// Server-side response
const server = http.createServer(function (req, res) {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hello World\n');
});
server.listen(12010);

server.on('upgrade', function (req, socket, upgradeHead) {
  const head = Buffer.alloc(upgradeHead.length);
  upgradeHead.copy(head);
  const key = req.headers['sec-websocket-key'];
  const shasum = crypto.createHash('sha1');
  const acceptKey = shasum.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11').digest('base64');
  const headers = [
    'HTTP/1.1 101 Switching Protocols',
    'Upgrade: websocket',
    'Connection: Upgrade',
    'Sec-WebSocket-Accept: ' + acceptKey,
    'Sec-WebSocket-Protocol: ' + protocol
  ];
  socket.setNoDelay(true);
  socket.write(headers.concat('', '').join('\r\n'));
  const websocket = new WebSocket();
  websocket.setSocket(socket);
});

File System (Fs) Module

The Node.js File System (fs) module provides both asynchronous and synchronous methods. For example, reading file contents can be done with asynchronous fs.readFile() or synchronous fs.readFileSync().

POSIX File System

MethodDescription
fs.truncateTruncates or extends a file to a specified length
fs.ftruncateLike truncate, but takes a file descriptor as a parameter
fs.chownChanges the owner and group of a file
fs.fchownLike chown, but takes a file descriptor as a parameter
fs.lchownLike chown, but does not resolve symbolic links
fs.statRetrieves file status
fs.lstatLike stat, but returns info about symbolic links
fs.fstatLike stat, but takes a file descriptor as a parameter
fs.linkCreates a hard link
fs.symlinkCreates a symbolic link
fs.readlinkReads the value of a symbolic link
fs.realpathReturns the canonicalized absolute pathname
fs.unlinkDeletes a file
fs.rmdirDeletes a directory
fs.mkdirCreates a directory
fs.readdirReads the contents of a directory
fs.closeCloses a file descriptor
fs.openOpens or creates a file for reading or writing
fs.utimesSets file access and modification times
fs.futimesLike utimes, but takes a file descriptor as a parameter
fs.fsyncSynchronizes file data to disk
fs.writeWrites data to a file
fs.readReads data from a file

Open File

fs.open(path, flags[, mode], callback)

Get File Information

const fs = require('fs');
fs.stat('/Users/liuht/code/itbilu/demo/fs.js', function (err, stats) {
  console.log(stats.isFile()); // true
});

Write File

fs.writeFile(file, data[, options], callback)
fs.writeFileSync(filename, data[, options])

Read File

fs.read(fd, buffer, offset, length, position, callback)
fs.readFile(filename[, options], callback)
fs.readFileSync(filename[, options])

Close File

fs.close(fd, callback)

Delete File

fs.unlink(path, callback)

Create Directory

fs.mkdir(path[, options], callback)

Read Directory

fs.readdir(path, callback)

Delete Directory

fs.rmdir(path, callback)
const fs = require('fs');
const buf = Buffer.alloc(1024);

console.log('Preparing to open an existing file!');
fs.open('input.txt', 'r+', function (err, fd) {
  if (err) {
    return console.error(err);
  }
  console.log('File opened successfully!');
  console.log('Preparing to read file:');
  fs.read(fd, buf, 0, buf.length, 0, function (err, bytes) {
    if (err) {
      console.log(err);
    }
    console.log(bytes + ' bytes read');
    if (bytes > 0) {
      console.log(buf.slice(0, bytes).toString());
    }
  });
});

Directory Traversal with fs.readdir and fs.stat

Use fs.readdir to read files and subdirectories in a directory, and fs.stat to check if each item is a file or directory. Here’s a recursive example:

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

function traverseDirectory(directory, callback) {
  fs.readdir(directory, (err, files) => {
    if (err) {
      callback(err);
      return;
    }

    let remainingFiles = files.length;

    if (remainingFiles === 0) {
      callback(null);
      return;
    }

    files.forEach(file => {
      fs.stat(path.join(directory, file), (err, stat) => {
        if (err) {
          callback(err);
          return;
        }

        if (stat.isDirectory()) {
          traverseDirectory(path.join(directory, file), callback);
        } else {
          console.log(file);
        }

        if (--remainingFiles === 0) {
          callback(null);
        }
      });
    });
  });
}

traverseDirectory('./some-directory', (err) => {
  if (err) {
    console.error('Error traversing directory:', err);
  } else {
    console.log('Directory traversal completed.');
  }
});

Asynchronous Traversal with fs.promises

The fs.promises API provides Promise-based file system operations for easier asynchronous handling.

const fs = require('fs').promises;
const path = require('path');

async function traverseDirectory(directory) {
  try {
    const files = await fs.readdir(directory);

    for (const file of files) {
      const filePath = path.join(directory, file);
      const stats = await fs.stat(filePath);

      if (stats.isDirectory()) {
        await traverseDirectory(filePath);
      } else {
        console.log(file);
      }
    }
  } catch (err) {
    console.error('Error traversing directory:', err);
  }
}

traverseDirectory('./some-directory')
  .then(() => console.log('Directory traversal completed.'));

Synchronous Traversal

Synchronous methods are not recommended for production but can be useful for quick prototyping or debugging.

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

function traverseDirectorySync(directory) {
  const files = fs.readdirSync(directory);

  for (const file of files) {
    const filePath = path.join(directory, file);
    const stats = fs.lstatSync(filePath);

    if (stats.isDirectory()) {
      traverseDirectorySync(filePath);
    } else {
      console.log(file);
    }
  }
}

try {
  traverseDirectorySync('./some-directory');
  console.log('Directory traversal completed.');
} catch (err) {
  console.error('Error traversing directory:', err);
}

HTTP Module

// 1. Import the http module
const http = require('http');
// 2. Create a web server instance
const server = http.createServer();
// 3. Start the server
server.listen(3000, () => {
  console.log('My server started');
});
// 4. Bind the request event to handle client requests
server.on('request', () => {
  console.log('Hello HTML');
});

Path Module

const path = require('path');
// Normalize path
console.log('normalization: ' + path.normalize('/test/test1//2slashes/1slash/tab/..'));
// Join paths
console.log('joint path: ' + path.join('/test', 'test1', '2slashes/1slash', 'tab', '..'));
// Resolve to absolute path
console.log('resolve: ' + path.resolve('main.js'));
// Get file extension
console.log('ext name: ' + path.extname('main.js'));

URL Module

The URL module provides three methods: url.parse(), url.format(), and url.resolve() for parsing URLs.

const url = require('url');
const adr = 'http://localhost:8888/index.html?year=2019&month=September';
const q = url.parse(adr, true);

console.log(q.host);
console.log(q.pathname);
console.log(q.search);

const qdata = q.query;
console.log(qdata.month);

Util Module

The Util module provides utility functions, including: util.callbackify, util.inherits, util.inspect, util.isArray(object), util.isRegExp(object), and util.isDate(object).

OS Module

const os = require('os');
// CPU byte order
console.log('endianness: ' + os.endianness());
// Operating system name
console.log('type: ' + os.type());
// Operating system platform
console.log('platform: ' + os.platform());
// Total system memory
console.log('total memory: ' + os.totalmem() + ' bytes.');
// Free system memory
console.log('free memory: ' + os.freemem() + ' bytes.');

Domain Module

const EventEmitter = require('events').EventEmitter;
const domain = require('domain');
const emitter1 = new EventEmitter();

// Create domain
const domain1 = domain.create();
domain1.on('error', function (err) {
  console.log('domain1 handles this error (' + err.message + ')');
});

// Explicit binding
domain1.add(emitter1);
emitter1.on('error', function (err) {
  console.log('Listener handles this error (' + err.message + ')');
});
emitter1.emit('error', new Error('Handled by listener'));
emitter1.removeAllListeners('error');
emitter1.emit('error', new Error('Handled by domain1'));

const domain2 = domain.create();
domain2.on('error', function (err) {
  console.log('domain2 handles this error (' + err.message + ')');
});

// Implicit binding
domain2.run(function () {
  const emitter2 = new EventEmitter();
  emitter2.emit('error', new Error('Handled by domain2'));
});

domain1.remove(emitter1);
emitter1.emit('error', new Error('Converted to exception, system will crash!'));

DNS Module

const dns = require('dns');
dns.lookup('www.github.com', function onLookup(err, address, family) {
  console.log('IP address:', address);
  dns.reverse(address, function (err, hostnames) {
    if (err) {
      console.log(err.stack);
    }
    console.log('Reverse lookup ' + address + ': ' + JSON.stringify(hostnames));
  });
});

Stream Module

Streams come in four types:

  • Readable: For read operations.
  • Writable: For write operations.
  • Duplex: For both read and write operations.
  • Transform: For writing data and reading transformed results.

Reading from a Stream

const fs = require('fs');
let data = '';
// Create readable stream
const readerStream = fs.createReadStream('input.txt');
// Set encoding to utf8
readerStream.setEncoding('UTF8');
// Handle stream events: data, end, error
readerStream.on('data', function (chunk) {
  data += chunk;
});
readerStream.on('end', function () {
  console.log(data);
});
readerStream.on('error', function (err) {
  console.log(err.stack);
});
console.log('Program execution completed');

Writing to a Stream

const fs = require('fs');
const data = 'Rookie Tutorial Website: www.runoob.com';
// Create a writable stream to output.txt
const writerStream = fs.createWriteStream('output.txt');
// Write data with utf8 encoding
writerStream.write(data, 'UTF8');
// Mark end of file
writerStream.end();
// Handle stream events: finish, error
writerStream.on('finish', function () {
  console.log('Write completed.');
});
writerStream.on('error', function (err) {
  console.log(err.stack);
});
console.log('Program execution completed');

Piping Streams

const fs = require('fs');
// Create readable stream
const readerStream = fs.createReadStream('input.txt');
// Create writable stream
const writerStream = fs.createWriteStream('output.txt');
// Pipe read to write: Read input.txt and write to output.txt
readerStream.pipe(writerStream);
console.log('Program execution completed');

Chaining Streams

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

// Compress input.txt to input.txt.gz
fs.createReadStream('input.txt')
  .pipe(zlib.createGzip())
  .pipe(fs.createWriteStream('input.txt.gz'));

console.log('File compression completed.');

Share your love