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:
-
Single Responsibility: Each module should do one thing well
-
Clear Exports: Export only what's needed
-
Error Handling: Handle errors within the module
-
Stateless Functions: Prefer pure functions when possible
-
Documentation: Add JSDoc comments
-
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!