Node.js organizes code into modules — each file is a separate module with its own scope.

CommonJS (Default in Node.js)

Exporting

  // math.js
function add(a, b) {
    return a + b;
}

function subtract(a, b) {
    return a - b;
}

module.exports = { add, subtract };

// Or export individually
exports.multiply = (a, b) => a * b;
  

Importing

  // app.js
const { add, subtract } = require('./math');
const math = require('./math');

console.log(add(2, 3));       // 5
console.log(math.subtract(5, 2)); // 3
  

ES Modules

Enable with "type": "module" in package.json or use .mjs extension.

  // math.js
export function add(a, b) {
    return a + b;
}

export default function multiply(a, b) {
    return a * b;
}

// app.js
import multiply, { add } from './math.js';
console.log(add(2, 3));
console.log(multiply(2, 3));
  

Note: ES modules require file extensions in import paths: './math.js'.

Built-in Modules

Node.js includes core modules — no installation needed:

  const fs = require('fs');           // File system
const path = require('path');       // Path utilities
const http = require('http');       // HTTP server/client
const os = require('os');           // OS information
const crypto = require('crypto');   // Cryptography
const events = require('events');   // EventEmitter
  

Or with ES modules:

  import fs from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
  

__dirname and __filename

In CommonJS:

  console.log(__filename); // full path to current file
console.log(__dirname);  // directory of current file
  

In ES modules, derive them manually (see example above).

Module Caching

Modules are cached after first load — subsequent require() returns the cached exports:

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

// app.js
const c1 = require('./counter');
const c2 = require('./counter');
c1.increment();
console.log(c2.getCount()); // 1 — same instance
  

Circular Dependencies

Avoid circular imports when possible. Node.js handles them but may return partially initialized exports.

Best Practices

  • One module = one responsibility
  • Use ES modules for new projects
  • Prefer named exports for clarity
  • Keep index.js as a barrel file re-exporting from a folder