HTML & JS Topic

Node.js Module System

CommonJS Modules (require, module.exports)

Node.js originally used CommonJS modules (still the default).

Exporting from Modules:

1. Exporting a single item:

javascript

// math.js
const add = (a, b) => a + b;

// Method 1: module.exports
module.exports = add;

// Method 2: exports shortcut (can't reassign exports directly)
// exports = add; // WRONG - won't work

2. Exporting multiple items:

javascript

// utils.js
const greet = (name) => `Hello ${name}`;
const add = (a, b) => a + b;
const PI = 3.14159;

// Export as object
module.exports = {
  greet,
  add,
  PI
};

// Or individually
module.exports.greet = greet;
module.exports.add = add;
module.exports.PI = PI;

// Or using exports shorthand (when not reassigning exports)
exports.greet = greet;
exports.add = add;
exports.PI = PI;

Importing Modules:

javascript

// app.js
// Import single export
const add = require('./math.js');
console.log(add(2, 3)); // 5

// Import multiple exports
const utils = require('./utils.js');
console.log(utils.greet('Alice')); // Hello Alice
console.log(utils.add(5, 3));      // 8

// Destructure imports
const { greet, PI } = require('./utils.js');
console.log(greet('Bob'), PI); // Hello Bob 3.14159

// Import built-in modules (no path needed)
const fs = require('fs');
const path = require('path');

// Import from node_modules (no path needed)
const express = require('express');
const lodash = require('lodash');

// Import JSON files (automatically parsed)
const config = require('./config.json');

Module Caching:

javascript

// Node.js caches modules - first require() is executed,
// subsequent requires return cached object

// counter.js
let count = 0;
module.exports = {
  increment: () => ++count,
  getCount: () => count
};

// app.js
const counter1 = require('./counter');
console.log(counter1.increment()); // 1

const counter2 = require('./counter'); // Same instance
console.log(counter2.getCount());      // 1 (not 0)

Circular Dependencies:

javascript

// a.js
console.log('a starting');
exports.done = false;
const b = require('./b.js');
console.log('in a, b.done =', b.done);
exports.done = true;
console.log('a done');

// b.js
console.log('b starting');
exports.done = false;
const a = require('./a.js');
console.log('in b, a.done =', a.done);
exports.done = true;
console.log('b done');

// main.js
console.log('main starting');
const a = require('./a.js');
const b = require('./b.js');
console.log('in main, a.done=', a.done, 'b.done=', b.done);

ES Modules (import, export)

Node.js supports ES Modules (ECMAScript modules) from v13.2+.

Enabling ES Modules:

json

// package.json
{
  "type": "module"  // Enables ES modules for all .js files
}

// OR use .mjs extension for ES modules
// OR use .cjs extension for CommonJS modules

Exporting with ES Modules:

1. Named exports:

javascript

// math.mjs or math.js (with "type": "module")
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
export const PI = 3.14159;

// Export existing variables
const multiply = (a, b) => a * b;
const divide = (a, b) => a / b;
export { multiply, divide };

// Rename exports
export { multiply as mul, divide as div };

2. Default export (one per module):

javascript

// calculator.js
const Calculator = {
  add: (a, b) => a + b,
  subtract: (a, b) => a - b
};

export default Calculator;

// OR export a single function as default
export default function greet(name) {
  return `Hello ${name}`;
}

3. Combined exports:

javascript

// utils.js
export const version = '1.0.0';

export default function mainFunction() {
  console.log('Main function');
}

// Re-export from another module
export { add, subtract } from './math.js';
export * from './math.js'; // Export everything except default

Importing with ES Modules:

javascript

// app.mjs or app.js (with "type": "module")

// Import named exports
import { add, PI } from './math.js';
console.log(add(2, 3), PI);

// Import with alias
import { add as sum, subtract as sub } from './math.js';

// Import all named exports as object
import * as math from './math.js';
console.log(math.add(5, 3), math.PI);

// Import default export
import Calculator from './calculator.js';
import greet from './utils.js';

// Mixed imports
import myDefault, { version } from './utils.js';
import myDefault, * as all from './utils.js';

// Import built-in modules (still need node: prefix or full import)
import fs from 'fs'; // CommonJS module - works with compatibility
import { readFile } from 'fs/promises'; // Promises API
import { createServer } from 'node:http'; // Node.js native modules with node: prefix

Dynamic Imports (Async):

javascript

// Load module conditionally or on-demand
const loadModule = async () => {
  if (condition) {
    const math = await import('./math.js');
    console.log(math.add(2, 3));
  }
};

// With destructuring
const { add } = await import('./math.js');

Built-in Modules Overview

Node.js comes with many built-in modules (no installation needed).

Core Modules:

1. File System (fs):

javascript

const fs = require('fs');
const fsPromises = require('fs/promises'); // Promise-based

// Synchronous methods (blocking)
const data = fs.readFileSync('file.txt', 'utf8');

// Asynchronous methods (non-blocking)
fs.readFile('file.txt', 'utf8', (err, data) => {
  if (err) throw err;
  console.log(data);
});

// Promise-based (recommended)
async function readFile() {
  try {
    const data = await fsPromises.readFile('file.txt', 'utf8');
    console.log(data);
  } catch (err) {
    console.error(err);
  }
}

2. Path (path):

javascript

const path = require('path');

console.log(path.join('/foo', 'bar', 'baz'));      // /foo/bar/baz
console.log(path.resolve('src', 'app.js'));        // /full/path/src/app.js
console.log(path.dirname('/foo/bar/baz.txt'));     // /foo/bar
console.log(path.basename('/foo/bar/baz.txt'));    // baz.txt
console.log(path.extname('index.html'));           // .html
console.log(path.parse('/home/user/file.txt'));
// { root: '/', dir: '/home/user', base: 'file.txt', ext: '.txt', name: 'file' }

3. HTTP (http / https):

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

// Create server
const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hello World');
});

server.listen(3000);

4. URL (url):

javascript

const url = require('url');

const myURL = new URL('https://example.com:8080/path?query=string#hash');
console.log(myURL.href);     // Full URL
console.log(myURL.hostname); // example.com
console.log(myURL.pathname); // /path
console.log(myURL.searchParams.get('query')); // string

// Legacy way
const parsed = url.parse('https://example.com/path');

5. Utilities (util):

javascript

const util = require('util');

// Callback to Promise
const readFile = util.promisify(fs.readFile);

// Debug logging
util.debuglog('app')(`Application started`);

// Inheritance
util.inherits(Child, Parent);

// Inspect objects
console.log(util.inspect(obj, { depth: 2, colors: true }));

6. Events (events):

javascript

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();
myEmitter.on('event', () => console.log('Event fired!'));
myEmitter.emit('event');

7. OS (os):

javascript

const os = require('os');

console.log(os.platform());  // 'darwin', 'win32', 'linux'
console.log(os.arch());      // 'x64', 'arm64'
console.log(os.cpus());      // CPU information
console.log(os.totalmem());  // Total memory
console.log(os.freemem());   // Free memory
console.log(os.homedir());   // Home directory
console.log(os.hostname());  // Hostname

8. Process (process): - Global, but also a module

javascript

// Already global, but can be required
const process = require('process');

Other Important Built-in Modules:

  • crypto: Encryption, hashing, security

  • stream: Handle streaming data

  • child_process: Run system commands

  • cluster: Create child processes for load balancing

  • zlib: Compression

  • buffer: Handle binary data

  • querystring: Parse query strings

  • assert: Testing utilities

Creating Custom Modules

1. Simple Module Example:

javascript

// logger.js
const logLevels = {
  INFO: 'INFO',
  WARN: 'WARN',
  ERROR: 'ERROR'
};

let currentLevel = logLevels.INFO;

function log(level, message) {
  if (Object.values(logLevels).indexOf(level) >= 
      Object.values(logLevels).indexOf(currentLevel)) {
    console.log(`[${level}] ${new Date().toISOString()}: ${message}`);
  }
}

function setLogLevel(level) {
  if (Object.values(logLevels).includes(level)) {
    currentLevel = level;
  }
}

// Export public API
module.exports = {
  logLevels,
  info: (msg) => log(logLevels.INFO, msg),
  warn: (msg) => log(logLevels.WARN, msg),
  error: (msg) => log(logLevels.ERROR, msg),
  setLogLevel
};

Using the logger:

javascript

// app.js
const logger = require('./logger');

logger.info('Application started');
logger.warn('Disk space running low');
logger.error('Failed to connect to database');

logger.setLogLevel(logger.logLevels.WARN);
logger.info('This will not be logged'); // Not logged due to level

2. Class-Based Module:

javascript

// database.js
class Database {
  constructor(connectionString) {
    this.connectionString = connectionString;
    this.isConnected = false;
  }

  connect() {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        this.isConnected = true;
        console.log('Connected to database');
        resolve();
      }, 1000);
    });
  }

  query(sql) {
    if (!this.isConnected) {
      throw new Error('Not connected to database');
    }
    return `Executed: ${sql}`;
  }
}

// Export the class
module.exports = Database;

// Or create and export instance
// const db = new Database('mysql://localhost:3306/mydb');
// module.exports = db;

Using the class:

javascript

// app.js
const Database = require('./database');

const db = new Database('mysql://localhost:3306/mydb');

async function run() {
  await db.connect();
  const result = db.query('SELECT * FROM users');
  console.log(result);
}

3. Configuration Module Pattern:

javascript

// config.js
// Singleton pattern - shared state across imports

let config = {
  apiUrl: 'https://api.example.com',
  timeout: 5000,
  retries: 3
};

function get(key) {
  return config[key];
}

function set(key, value) {
  config[key] = value;
  return config;
}

function getAll() {
  return { ...config }; // Return copy to prevent mutation
}

// Private function (not exported)
function validateConfig(newConfig) {
  // Validation logic
  return true;
}

module.exports = {
  get,
  set,
  getAll
};

4. Factory Pattern Module:

javascript

// payment.js
function createPaymentProcessor(type) {
  switch(type) {
    case 'stripe':
      return {
        charge: (amount) => `Charged $${amount} via Stripe`,
        refund: (amount) => `Refunded $${amount} via Stripe`
      };
    case 'paypal':
      return {
        charge: (amount) => `Charged $${amount} via PayPal`,
        refund: (amount) => `Refunded $${amount} via PayPal`
      };
    default:
      throw new Error(`Unknown payment processor: ${type}`);
  }
}

// Export factory function
module.exports = createPaymentProcessor;

Using the factory:

javascript

const createPaymentProcessor = require('./payment');

const stripe = createPaymentProcessor('stripe');
console.log(stripe.charge(100)); // Charged $100 via Stripe

Best Practices for Custom Modules:

  1. Single Responsibility: Each module should do one thing well

  2. Clear Exports: Export only what's needed

  3. Error Handling: Handle errors within the module

  4. Stateless Functions: Prefer pure functions when possible

  5. Documentation: Add JSDoc comments

  6. Testing: Make modules testable

Module Resolution:

javascript

// Node.js searches for modules in this order:
// 1. Core modules (fs, path, etc.)
// 2. File modules (./, ../, /)
// 3. Directory modules (looks for package.json or index.js)
// 4. node_modules folders (up the directory tree)

// require() resolution:
require('./file')              // file.js, file.json, file.node
require('./directory')         // directory/index.js, directory/package.json
require('module')              // node_modules/module/index.js
require('module/lib/file')     // node_modules/module/lib/file.js

This module system is fundamental to Node.js development and enables code organization, reusability, and maintainability!

 

Comments (0)

No comments yet. Be the first to comment!

LecturreAI Assistant

Hello! I'm LecturreAI

What would you like to learn today?