Node.js Core Modules
Node.js provides built-in modules you can use immediately after installation. You can load them with require() without any npm install.
fs Module: File System
The fs (File System) module handles all file system operations: reading, writing, and managing directories.
Three Styles
const fs = require('fs');
const fsPromises = require('fs/promises');
// 1. Callback style (traditional)
fs.readFile('./data.txt', 'utf8', (err, data) => {
if (err) {
console.error('Read failed:', err.message);
return;
}
console.log('Callback:', data);
});
// 2. Synchronous style (blocking, only recommended at startup)
try {
const data = fs.readFileSync('./data.txt', 'utf8');
console.log('Sync:', data);
} catch (err) {
console.error('Read failed:', err.message);
}
// 3. Promise/async-await style (recommended)
async function readFileAsync() {
try {
const data = await fsPromises.readFile('./data.txt', 'utf8');
console.log('Promise:', data);
} catch (err) {
console.error('Read failed:', err.message);
}
}
readFileAsync();
Writing Files
const fs = require('fs/promises');
async function writeExample() {
// Create or overwrite a file
await fs.writeFile('./output.txt', 'Hello!\n', 'utf8');
// Append content to a file
await fs.appendFile('./output.txt', 'Appended content\n', 'utf8');
// Copy a file
await fs.copyFile('./output.txt', './backup.txt');
// Rename / move a file
await fs.rename('./output.txt', './renamed.txt');
// Delete a file
await fs.unlink('./renamed.txt');
console.log('File operations complete');
}
writeExample();
Directory Operations
const fs = require('fs/promises');
const path = require('path');
async function dirExample() {
const dirPath = './test-dir';
// Create directory (nested paths in one call)
await fs.mkdir(path.join(dirPath, 'sub', 'nested'), { recursive: true });
console.log('Directory created');
// Read directory contents
const entries = await fs.readdir(dirPath, { withFileTypes: true });
for (const entry of entries) {
const type = entry.isDirectory() ? 'dir' : 'file';
console.log(` [${type}] ${entry.name}`);
}
// Get file/directory info
const stat = await fs.stat(dirPath);
console.log('Created at:', stat.birthtime);
console.log('Modified at:', stat.mtime);
console.log('Is directory:', stat.isDirectory());
// Remove directory (including contents with recursive)
await fs.rm(dirPath, { recursive: true, force: true });
console.log('Directory removed');
}
dirExample();
Hands-On: Recursive Directory Listing
const fs = require('fs/promises');
const path = require('path');
async function walkDir(dirPath, indent = 0) {
const entries = await fs.readdir(dirPath, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dirPath, entry.name);
const prefix = ' '.repeat(indent);
if (entry.isDirectory()) {
console.log(`${prefix}[DIR] ${entry.name}/`);
await walkDir(fullPath, indent + 1);
} else {
const stat = await fs.stat(fullPath);
const size = (stat.size / 1024).toFixed(1);
console.log(`${prefix}[FILE] ${entry.name} (${size} KB)`);
}
}
}
walkDir('./src').catch(console.error);
path Module
Safely manipulates path strings. Since OS path separators differ (/ vs \), always use path instead of raw string manipulation.
const path = require('path');
// Join paths (handles OS separator automatically)
console.log(path.join('/users', 'kim', 'documents', 'file.txt'));
// /users/kim/documents/file.txt
// Resolve to absolute path
console.log(path.resolve('./src', 'utils', 'helper.js'));
// /current-working-dir/src/utils/helper.js
// Decompose a path
const filePath = '/home/user/project/src/app.js';
console.log(path.dirname(filePath)); // /home/user/project/src
console.log(path.basename(filePath)); // app.js
console.log(path.extname(filePath)); // .js
console.log(path.basename(filePath, '.js')); // app (no extension)
// Parse a path
const parsed = path.parse(filePath);
console.log(parsed);
// { root: '/', dir: '/home/user/project/src', base: 'app.js', ext: '.js', name: 'app' }
// Reconstruct a path
console.log(path.format(parsed)); // /home/user/project/src/app.js
// Compute relative path
console.log(path.relative('/data/orandea/test/aaa', '/data/orandea/impl/bbb'));
// ../../impl/bbb
// Use with __dirname (path relative to current file)
const configPath = path.join(__dirname, '..', 'config', 'app.json');
console.log(configPath);
os Module
Retrieves operating system information. Useful for detecting deployment environments and monitoring resources.
const os = require('os');
// Basic info
console.log('Platform:', os.platform()); // linux, win32, darwin
console.log('Architecture:', os.arch()); // x64, arm64
console.log('OS version:', os.version());
console.log('Hostname:', os.hostname());
console.log('Home directory:', os.homedir()); // /home/user
console.log('Temp directory:', os.tmpdir()); // /tmp
// Memory info
const totalMem = os.totalmem();
const freeMem = os.freemem();
const usedMem = totalMem - freeMem;
console.log(`Memory: ${(usedMem / 1e9).toFixed(2)}GB / ${(totalMem / 1e9).toFixed(2)}GB used`);
// CPU info
const cpus = os.cpus();
console.log(`CPU: ${cpus[0].model} x ${cpus.length} cores`);
// Network interfaces
const nets = os.networkInterfaces();
for (const [name, addrs] of Object.entries(nets)) {
for (const addr of addrs) {
if (addr.family === 'IPv4' && !addr.internal) {
console.log(`${name}: ${addr.address}`);
}
}
}
// Uptime
const uptime = os.uptime();
const hours = Math.floor(uptime / 3600);
const minutes = Math.floor((uptime % 3600) / 60);
console.log(`System uptime: ${hours}h ${minutes}m`);
// Load average (Linux/macOS only)
if (os.platform() !== 'win32') {
console.log('Load averages:', os.loadavg());
}
crypto Module
Provides cryptographic functionality: hashing, HMAC, random byte generation, encryption/decryption.
Generating Hashes
const crypto = require('crypto');
// MD5 hash (do NOT use for security, checksums only)
const md5 = crypto.createHash('md5')
.update('Hello, World!')
.digest('hex');
console.log('MD5:', md5);
// SHA-256 (use bcrypt for passwords, SHA-256 for general hashing)
const sha256 = crypto.createHash('sha256')
.update('my-password-123')
.digest('hex');
console.log('SHA-256:', sha256);
// Stream-based file hash
const fs = require('fs');
function hashFile(filePath) {
return new Promise((resolve, reject) => {
const hash = crypto.createHash('sha256');
const stream = fs.createReadStream(filePath);
stream.on('data', chunk => hash.update(chunk));
stream.on('end', () => resolve(hash.digest('hex')));
stream.on('error', reject);
});
}
Generating Random Values
const crypto = require('crypto');
// Random bytes (for token generation)
const token = crypto.randomBytes(32).toString('hex');
console.log('Token:', token); // 64-character hex string
// Random UUID
const uuid = crypto.randomUUID();
console.log('UUID:', uuid); // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
// Random integer in range
const randomInt = crypto.randomInt(1, 100);
console.log('Random int:', randomInt);
HMAC (Message Authentication Code)
const crypto = require('crypto');
const secretKey = 'my-secret-key';
const message = '{"user":"admin","action":"login"}';
// Generate HMAC
const hmac = crypto.createHmac('sha256', secretKey)
.update(message)
.digest('hex');
console.log('HMAC:', hmac);
// Verify HMAC
function verifyHmac(message, signature, key) {
const expected = crypto.createHmac('sha256', key)
.update(message)
.digest('hex');
// Use timingSafeEqual to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}
console.log('Valid:', verifyHmac(message, hmac, secretKey)); // true
stream Module: Brief Introduction
Streams process data in small chunks, allowing large data to be handled without loading everything into memory at once. (See streams.md for full details.)
const fs = require('fs');
// Pipe: read stream → write stream
const readStream = fs.createReadStream('./large-file.txt');
const writeStream = fs.createWriteStream('./copy.txt');
readStream.pipe(writeStream);
writeStream.on('finish', () => {
console.log('File copy complete');
});
events Module: EventEmitter Pattern
Many Node.js core modules are built on top of EventEmitter. http.Server, fs.ReadStream, and net.Socket all extend EventEmitter.
Basic Usage
const EventEmitter = require('events');
// Using EventEmitter directly
const emitter = new EventEmitter();
// Register an event listener
emitter.on('data', (payload) => {
console.log('Data received:', payload);
});
// Listener that fires only once
emitter.once('connect', () => {
console.log('First connection!');
});
// Emit events
emitter.emit('data', { id: 1, message: 'Hello' });
emitter.emit('connect'); // fires
emitter.emit('connect'); // does NOT fire (once)
// Remove a listener
const handler = (data) => console.log(data);
emitter.on('message', handler);
emitter.off('message', handler); // remove
Inheriting EventEmitter
In practice, classes typically extend EventEmitter:
const EventEmitter = require('events');
class Database extends EventEmitter {
constructor(host) {
super();
this.host = host;
this.connected = false;
}
connect() {
setTimeout(() => {
this.connected = true;
this.emit('connect', { host: this.host });
}, 100);
}
query(sql) {
if (!this.connected) {
this.emit('error', new Error('Not connected to database'));
return;
}
setTimeout(() => {
const result = [{ id: 1, name: 'Test' }];
this.emit('result', { sql, result });
}, 50);
}
disconnect() {
this.connected = false;
this.emit('disconnect');
}
}
// Usage
const db = new Database('localhost:5432');
db.on('connect', ({ host }) => {
console.log(`Database connected: ${host}`);
db.query('SELECT * FROM users');
});
db.on('result', ({ sql, result }) => {
console.log(`Query result [${sql}]:`, result);
db.disconnect();
});
db.on('disconnect', () => {
console.log('Connection closed');
});
db.on('error', (err) => {
console.error('Error:', err.message);
});
db.connect();
Handling Max Listener Warnings
const EventEmitter = require('events');
const emitter = new EventEmitter();
// Default max listeners: 10 (warning if exceeded)
emitter.setMaxListeners(20); // increase limit
// Or set globally
EventEmitter.defaultMaxListeners = 20;
// Check current listener count
console.log('Listener count:', emitter.listenerCount('data'));
// List all registered event names
console.log('Event names:', emitter.eventNames());
Pro Tips
When to Use Built-in vs npm Packages
// Built-in is enough:
const { createHash } = require('crypto'); // for general hashing
const { join } = require('path'); // path operations
// npm package is needed:
// bcrypt: password hashing (manages salt + iteration count)
// sharp: image processing
// multer: file upload handling
Using the util Module
const util = require('util');
const fs = require('fs');
// Convert callback functions to Promises
const readFilePromise = util.promisify(fs.readFile);
async function main() {
const data = await readFilePromise('./package.json', 'utf8');
console.log(JSON.parse(data).name);
}
main();
// Deep object inspection (more detailed than console.log)
const complex = { a: { b: { c: { d: 'deep' } } } };
console.log(util.inspect(complex, { depth: null, colors: true }));
// Type checking
console.log(util.types.isPromise(Promise.resolve())); // true
console.log(util.types.isGeneratorFunction(function* () {})); // true
Understanding Module Caching
// Node.js caches modules after the first require
const mod1 = require('./config');
const mod2 = require('./config'); // loaded from cache, same object
console.log(mod1 === mod2); // true
// Force cache invalidation (useful in tests)
delete require.cache[require.resolve('./config')];
const mod3 = require('./config'); // freshly loaded