> ## Documentation Index
> Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 02. Modules System

> Understand the Node.js module system, CommonJS, and how to create reusable code.

<img src="https://mintcdn.com/devweeekends/X0Fp4X8lMl-ZftoO/images/courses/node-crash-course/module-system.svg?fit=max&auto=format&n=X0Fp4X8lMl-ZftoO&q=85&s=c0826c4963df4cbc3c98926cd7255514" alt="Node.js Module System" width="1080" height="1080" data-path="images/courses/node-crash-course/module-system.svg" />

# Modules in Node.js

Modules are the building blocks of any Node.js application. Think of them like LEGO bricks: each module is a self-contained piece with a specific shape and purpose. You snap them together to build something larger, and you can swap one brick for another without rebuilding the whole structure. Without modules, you would end up with one massive file where every function can see and accidentally break every other function--the programming equivalent of storing your entire house in a single room.

Node.js uses the **CommonJS** module system by default, though it also supports **ES Modules** (ESM) in newer versions.

## The `require` Function

To include a module, use the `require()` function with the name of the module.

```javascript theme={null}
const fs = require('fs'); // Built-in module
```

## Creating Custom Modules

Let's create a simple module that exports some math functions.

**math.js**

```javascript theme={null}
const add = (a, b) => a + b;
const subtract = (a, b) => a - b;

// Exporting functions
module.exports = {
  add,
  subtract
};
```

**app.js**

```javascript theme={null}
const math = require('./math');

console.log(math.add(5, 3));      // Output: 8
console.log(math.subtract(5, 3)); // Output: 2
```

### Alternative Export Syntax

You can also attach properties directly to `exports` (which is a shorthand for `module.exports`).

```javascript theme={null}
exports.multiply = (a, b) => a * b;
exports.divide = (a, b) => a / b;
```

<Warning>
  **Pitfall:** Never reassign `exports` directly (e.g., `exports = { multiply, divide }`). The `exports` variable is just a reference to `module.exports`. Reassigning it breaks the reference, and your exports silently disappear. Always use `module.exports = ...` when exporting a single object or class, and `exports.name = ...` only when attaching individual properties.
</Warning>

## Module Wrapper Function

Under the hood, Node.js doesn't execute your code directly. It wraps it inside a function wrapper:

```javascript theme={null}
(function(exports, require, module, __filename, __dirname) {
  // Your module code actually lives in here
});
```

This is why variables defined in a module are scoped to that module (private) rather than being global. This wrapper function is also why you have access to `__filename` and `__dirname` without declaring them--they are parameters injected by Node.js, not magic globals. Understanding this wrapper is key to understanding why `var x = 5` in a module does not pollute the global scope the way it would in a browser script tag.

## Built-in Modules

Node.js comes with many useful built-in modules. We will explore these in depth in later chapters, but here are a few common ones:

* `fs`: File system operations.
* `http`: Create HTTP servers.
* `path`: Utilities for working with file and directory paths.
* `os`: Operating system information.
* `events`: Event emitter.

### Example: The `os` Module

```javascript theme={null}
const os = require('os');

console.log('Platform:', os.platform());
console.log('Architecture:', os.arch());
console.log('Free Memory:', os.freemem());
console.log('Total Memory:', os.totalmem());
console.log('Uptime:', os.uptime());
```

## ES Modules (import/export)

Node.js has added support for ES Modules, which is the standard in modern JavaScript (and browsers).

To use ES Modules, you can either:

1. Use the `.mjs` extension for your files.
2. Add `"type": "module"` to your `package.json`.

**math.mjs**

```javascript theme={null}
export const add = (a, b) => a + b;
export default function log(msg) {
  console.log(msg);
}
```

**app.mjs**

```javascript theme={null}
import log, { add } from './math.mjs';

log('Result: ' + add(2, 3));
```

## CommonJS vs ES Modules Comparison

| Feature                      | CommonJS                             | ES Modules                                                                          |
| ---------------------------- | ------------------------------------ | ----------------------------------------------------------------------------------- |
| Syntax                       | `require()` / `module.exports`       | `import` / `export`                                                                 |
| Loading                      | Synchronous                          | Asynchronous                                                                        |
| File Extension               | `.js` or `.cjs`                      | `.mjs` or `.js` with "type": "module"                                               |
| Top-level await              | Not supported                        | Supported                                                                           |
| Dynamic imports              | `require(variable)`                  | `import(variable)`                                                                  |
| Tree shaking                 | Limited                              | Supported                                                                           |
| Browser support              | No                                   | Yes                                                                                 |
| `__dirname` / `__filename`   | Available directly                   | Must use `import.meta.url` + `fileURLToPath`                                        |
| Conditional exports          | `if (x) require('a')` works anywhere | Static imports must be at top level; use dynamic `import()` for conditional loading |
| Circular dependency handling | Returns partially-loaded module      | Throws `ReferenceError` if binding not yet initialized                              |
| Default export               | `module.exports = value`             | `export default value`                                                              |

### Module System Decision Framework

**Start a new project today?** Use ES Modules (`"type": "module"` in package.json). The ecosystem is moving toward ESM, bundlers work better with it, and it aligns with browser JavaScript.

**Maintaining an existing CommonJS project?** Stay with CommonJS unless you have a compelling reason to migrate. Converting a large codebase is non-trivial: you need to update every `require` to `import`, handle `__dirname` replacements, deal with libraries that only ship CommonJS, and fix circular dependencies that worked under CJS but break under ESM.

**Publishing an NPM package?** Ship both formats using the `exports` field in package.json (conditional exports). This lets CJS and ESM consumers both use your package without issues.

**Edge case -- CJS/ESM interop:** You can `import` a CommonJS module from ESM (Node.js wraps it as a default export). But you cannot `require()` an ES module from CommonJS -- you must use `await import()` inside an async function instead. This asymmetry trips up many teams during migration.

## Module Resolution: How Node.js Finds Your Modules

When you call `require('something')`, Node.js follows a specific resolution algorithm. Understanding it prevents mysterious "module not found" errors:

| What you write         | What Node.js does                                                            | Example         |
| ---------------------- | ---------------------------------------------------------------------------- | --------------- |
| `require('./foo')`     | Looks for `./foo.js`, then `./foo/index.js` in the same directory            | Local module    |
| `require('fs')`        | Checks built-in modules first (always wins over NPM packages with same name) | Built-in module |
| `require('lodash')`    | Walks up the directory tree checking `node_modules/` at each level           | NPM package     |
| `require('/abs/path')` | Uses the absolute path directly                                              | Absolute path   |

**Edge case -- the `node_modules` crawl:** If your project is at `/app/src/utils/helper.js` and you `require('lodash')`, Node.js checks: `/app/src/utils/node_modules/lodash`, then `/app/src/node_modules/lodash`, then `/app/node_modules/lodash`, then `/node_modules/lodash`. This crawl is why deeply nested `node_modules` caused performance problems in early Node.js and why tools like pnpm use symlinks instead.

## Module Caching

Node.js caches modules after the first `require()`. Subsequent calls return the cached version.

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

// app.js
const counter1 = require('./counter');
const counter2 = require('./counter');

counter1.increment();
counter1.increment();
console.log(counter2.getCount()); // Output: 2 (same instance!)
```

<Warning>
  Module caching can lead to unexpected behavior if you expect fresh instances. This is the "accidental singleton" pitfall--because `require()` returns the same cached object every time, any module that holds state (counters, connections, configuration) becomes a shared singleton across your entire app. If you need independent instances, export a factory function or a class instead:

  ```javascript theme={null}
  // Instead of exporting an object with state directly:
  module.exports = () => {
    let count = 0;
    return { increment: () => ++count, getCount: () => count };
  };
  ```
</Warning>

## Circular Dependencies

Node.js handles circular dependencies, but they can cause issues.

```javascript theme={null}
// 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
const a = require('./a.js');
const b = require('./b.js');

// Output:
// a starting
// b starting
// in b, a.done = false  <-- Incomplete!
// b done
// in a, b.done = true
// a done
```

<Tip>
  **Best Practice**: Avoid circular dependencies by restructuring your code. Extract shared code into a separate module.
</Tip>

## The `path` Module

The `path` module provides utilities for working with file and directory paths.

```javascript theme={null}
const path = require('path');

// Join paths (handles OS-specific separators)
const filePath = path.join(__dirname, 'data', 'users.json');
console.log(filePath); // /app/data/users.json (Unix) or \app\data\users.json (Windows)

// Resolve to absolute path
const absolute = path.resolve('data', 'users.json');
console.log(absolute); // Full absolute path

// Get file extension
console.log(path.extname('file.txt')); // .txt

// Get filename without extension
console.log(path.basename('file.txt', '.txt')); // file

// Get directory name
console.log(path.dirname('/users/john/file.txt')); // /users/john

// Parse path into components
const parsed = path.parse('/home/user/file.txt');
console.log(parsed);
// {
//   root: '/',
//   dir: '/home/user',
//   base: 'file.txt',
//   ext: '.txt',
//   name: 'file'
// }
```

## The `url` Module

```javascript theme={null}
const { URL } = require('url');

const myUrl = new URL('https://example.com:8080/path?name=John&age=30#section');

console.log(myUrl.href);       // Full URL
console.log(myUrl.protocol);   // https:
console.log(myUrl.host);       // example.com:8080
console.log(myUrl.hostname);   // example.com
console.log(myUrl.port);       // 8080
console.log(myUrl.pathname);   // /path
console.log(myUrl.search);     // ?name=John&age=30
console.log(myUrl.hash);       // #section

// URLSearchParams
console.log(myUrl.searchParams.get('name'));  // John
myUrl.searchParams.append('country', 'USA');
console.log(myUrl.href);
```

## Creating a Reusable Module

Let's create a practical utility module:

```javascript theme={null}
// utils/logger.js
const colors = {
  reset: '\x1b[0m',
  red: '\x1b[31m',
  green: '\x1b[32m',
  yellow: '\x1b[33m',
  blue: '\x1b[34m'
};

const formatDate = () => new Date().toISOString();

const logger = {
  info: (msg) => console.log(`${colors.blue}[INFO]${colors.reset} ${formatDate()} - ${msg}`),
  success: (msg) => console.log(`${colors.green}[SUCCESS]${colors.reset} ${formatDate()} - ${msg}`),
  warn: (msg) => console.log(`${colors.yellow}[WARN]${colors.reset} ${formatDate()} - ${msg}`),
  error: (msg) => console.log(`${colors.red}[ERROR]${colors.reset} ${formatDate()} - ${msg}`)
};

module.exports = logger;

// Usage:
// const logger = require('./utils/logger');
// logger.info('Server started');
// logger.error('Database connection failed');
```

## Summary

* Node.js uses **CommonJS** (`require`/`module.exports`) by default
* **ES Modules** (`import`/`export`) are also supported and are becoming the standard for new projects
* Modules are **cached** after first load--making every stateful module an accidental singleton
* Avoid **circular dependencies** by extracting shared logic into a separate module
* Use the **`path`** module for cross-platform file paths (never concatenate paths with string `+`)
* Use the **`url`** module for URL parsing and manipulation
* Node.js wraps every module in a **wrapper function**, providing local scope and variables like `__dirname`
* When starting a new project, prefer ES Modules (`"type": "module"` in package.json) for tree-shaking support and alignment with the broader JavaScript ecosystem
