# Understanding the Purpose and Typical Structure of a Node Module

> Discover the purpose and structure of Node modules. Learn how npm uses package.json, entry points, and node_modules for code encapsulation, distribution, and dependency management.

- Repository: [Node.js/node](https://github.com/nodejs/node)
- Tags: deep-dive
- Published: 2026-02-19

---

**A Node module bundles JavaScript code with metadata to enable encapsulation, versioned distribution, and dependency management through npm, typically structured around a [`package.json`](https://github.com/nodejs/node/blob/main/package.json) file, entry point, and `node_modules` directory.**

When working with npm, understanding the purpose and typical structure of a node module is essential for creating reusable, maintainable code. According to the `nodejs/node` repository source code, a Node module represents the fundamental unit of distribution that powers the entire npm ecosystem, combining implementation files with descriptive metadata that tells Node.js how to load and execute the code.

## What Is a Node Module?

A Node module is a reusable unit of code that combines implementation files with metadata describing its interface and dependencies. As documented in [`doc/api/modules.md`](https://github.com/nodejs/node/blob/main/doc/api/modules.md), Node.js implements two module systems—CommonJS (`require`) and ECMAScript Modules (`import`)—both of which rely on the package structure defined in [`doc/api/packages.md`](https://github.com/nodejs/node/blob/main/doc/api/packages.md).

The module system enables JavaScript files to share functionality without polluting the global namespace, creating a clean boundary between implementation details and public APIs.

## Core Purpose of Node Modules

Node modules serve four primary functions that enable scalable JavaScript development:

**Encapsulation** hides internal implementation behind a public API. Consumers load the module via `require()` or `import` without accessing private files, as the module system restricts visibility to explicitly exported members.

**Versioned Distribution** allows developers to publish specific releases to the npm registry. Consumers declare semantic version ranges in [`package.json`](https://github.com/nodejs/node/blob/main/package.json), receiving compatible updates automatically while avoiding breaking changes.

**Dependency Management** enables modules to declare external requirements. As implemented in `deps/npm/`, npm resolves these declarations into a nested `node_modules` tree, ensuring each module receives its own copy of its dependencies to prevent version conflicts.

**Tooling Integration** supports development workflows through npm scripts. Commands defined in [`package.json`](https://github.com/nodejs/node/blob/main/package.json) under the `"scripts"` field standardize testing, building, and publishing operations across different environments.

## Typical File System Structure

A conventional Node module follows this directory layout:

```

my-library/
├─ package.json          # metadata, scripts, dependencies

├─ README.md             # human-readable description

├─ LICENSE               # legal information

├─ .gitignore            # files npm should ignore

├─ index.js (or lib/)    # entry point defined by "main"/"exports"

├─ lib/                  # implementation files (.js, .cjs, .mjs)

│   └─ …                 # internal modules

├─ bin/                  # optional executable scripts

├─ test/                 # test suite (run via "npm test")

└─ node_modules/         # installed dependencies (filled by npm)

```

## Module Resolution and Entry Points

Node.js resolves modules using algorithms documented in [`doc/api/modules.md`](https://github.com/nodejs/node/blob/main/doc/api/modules.md) and implemented in [`lib/internal/modules/cjs/loader.js`](https://github.com/nodejs/node/blob/main/lib/internal/modules/cjs/loader.js) and [`lib/internal/modules/esm/loader.js`](https://github.com/nodejs/node/blob/main/lib/internal/modules/esm/loader.js).

**Folder-as-Module**: When a directory contains a [`package.json`](https://github.com/nodejs/node/blob/main/package.json) with a `"main"` field, Node treats the folder as a module, loading the specified entry point. Without [`package.json`](https://github.com/nodejs/node/blob/main/package.json), Node falls back to [`index.js`](https://github.com/nodejs/node/blob/main/index.js) or `index.node`.

**Package Exports**: The `"exports"` field in [`package.json`](https://github.com/nodejs/node/blob/main/package.json), documented in [`doc/api/packages.md`](https://github.com/nodejs/node/blob/main/doc/api/packages.md), provides conditional entry points for different environments. It allows separate exports for CommonJS (`require`) versus ES Modules (`import`), and restricts consumer access to only explicitly listed paths.

## How npm Orchestrates Module Lifecycle

The npm client, located in `deps/npm/`, interacts with module structure through three primary operations:

**Installation**: `npm install` reads [`package.json`](https://github.com/nodejs/node/blob/main/package.json), resolves semantic version ranges against the registry, and constructs a `node_modules/` tree where each dependency resides in its own directory.

**Publishing**: `npm publish` packages the directory contents—excluding files matched by `.gitignore` and `.npmignore`—and uploads the tarball to the npm registry.

**Script Execution**: `npm run <script>` executes commands defined in the `"scripts"` section of [`package.json`](https://github.com/nodejs/node/blob/main/package.json), standardizing workflows like `npm test` across different projects.

## Practical Code Examples

The following examples demonstrate a complete Node module structure:

```json
// package.json – minimal module definition
{
  "name": "my-library",
  "version": "1.0.0",
  "description": "A utility library",
  "main": "lib/index.js",
  "scripts": {
    "test": "node test/run.js"
  },
  "dependencies": {
    "lodash": "^4.17.21"
  },
  "bin": {
    "my-cli": "./bin/cli.js"
  },
  "license": "MIT"
}

```

```javascript
// lib/index.js – public API implementation
const _ = require('lodash');

function hello(name) {
  return `Hello, ${_.capitalize(name)}!`;
}

// Export the API (CommonJS)
module.exports = { hello };

```

```javascript
// consumer.js – how users consume the module
const myLib = require('my-library');
console.log(myLib.hello('world'));     // → "Hello, World!"

```

## Key Source Files in the Node.js Repository

Understanding the module system requires examining these specific files in the `nodejs/node` repository:

| File | Purpose |
|------|---------|
| [`doc/api/modules.md`](https://github.com/nodejs/node/blob/main/doc/api/modules.md) | Documents CommonJS module loading, resolution algorithm, and the module wrapper |
| [`doc/api/packages.md`](https://github.com/nodejs/node/blob/main/doc/api/packages.md) | Defines [`package.json`](https://github.com/nodejs/node/blob/main/package.json) fields including `main`, `exports`, and `type` |
| [`lib/internal/modules/cjs/loader.js`](https://github.com/nodejs/node/blob/main/lib/internal/modules/cjs/loader.js) | Core implementation of `require()` and CommonJS module loading |
| [`lib/internal/modules/esm/loader.js`](https://github.com/nodejs/node/blob/main/lib/internal/modules/esm/loader.js) | Core implementation of ES Module loading and resolution |
| `deps/npm/` | npm client source code handling package installation, publishing, and lifecycle scripts |

## Summary

- A **Node module** combines JavaScript code with metadata to create reusable, distributable packages managed by npm.
- The **purpose** centers on encapsulation, versioned distribution, dependency management, and tooling integration.
- The **typical structure** includes [`package.json`](https://github.com/nodejs/node/blob/main/package.json) for metadata, an entry point file (specified by `main` or `exports`), implementation directories, and a `node_modules` folder for dependencies.
- Node.js resolves modules using the **folder-as-module** convention and the **package exports** field, implemented in [`lib/internal/modules/cjs/loader.js`](https://github.com/nodejs/node/blob/main/lib/internal/modules/cjs/loader.js) and [`lib/internal/modules/esm/loader.js`](https://github.com/nodejs/node/blob/main/lib/internal/modules/esm/loader.js).
- npm orchestrates the module lifecycle through **installation**, **publishing**, and **script execution** as implemented in `deps/npm/`.

## Frequently Asked Questions

### What is the difference between a Node module and a package?

A **package** refers to the distributable unit—a directory containing a [`package.json`](https://github.com/nodejs/node/blob/main/package.json) file and associated code—while a **module** specifically refers to the code unit that Node.js loads via `require()` or `import`. In practice, the terms are often used interchangeably because every package contains at least one module, but technically a single package can contain multiple modules (files) and a module does not necessarily need to be a package if it is a local file.

### How does Node.js resolve a module when I use require()?

Node.js follows the resolution algorithm documented in [`doc/api/modules.md`](https://github.com/nodejs/node/blob/main/doc/api/modules.md) and implemented in [`lib/internal/modules/cjs/loader.js`](https://github.com/nodejs/node/blob/main/lib/internal/modules/cjs/loader.js). For core modules like `fs` or `http`, it loads built-in bindings directly. For file modules, it resolves relative paths ([`./file.js`](https://github.com/nodejs/node/blob/main/./file.js)) or absolute paths. For directory modules (packages), it looks for [`package.json`](https://github.com/nodejs/node/blob/main/package.json) with a `main` field, falling back to [`index.js`](https://github.com/nodejs/node/blob/main/index.js) or `index.node`. If the module is not found locally, Node searches parent `node_modules` directories up the file system hierarchy.

### What is the purpose of the exports field in package.json?

The `exports` field, documented in [`doc/api/packages.md`](https://github.com/nodejs/node/blob/main/doc/api/packages.md), provides a modern alternative to `main` that enables conditional exports based on the consuming environment. It allows package authors to define different entry points for CommonJS (`require`) versus ES Modules (`import`), or for Node.js versus browser environments. This field also restricts what consumers can import—only paths explicitly listed in `exports` are accessible, providing better encapsulation than the traditional `main` field which exposes the entire package directory.

### Why does each module have its own node_modules directory?

npm creates a nested `node_modules` structure to prevent dependency conflicts and ensure each module receives the specific versions it requires. As implemented in `deps/npm/`, the package manager resolves the dependency tree and installs each dependency in the `node_modules` folder of the consuming package. This isolation means Module A can depend on Lodash v4 while Module B depends on Lodash v3 without version collisions. Node.js resolves modules by walking up the `node_modules` hierarchy from the requiring file's location, ensuring the nearest installed version is loaded first.