| |

npm 01 ๐ŸŒ What is npm and Node Modules

โฑ๏ธ Estimated read: 11 min | |

npm is the package manager for the Node JavaScript platform. Its job is to put modules in place so that Node.js can find them, and to manage the dependency conflicts that arise when different packages require different versions of the same library . The name originally stood for “Node Package Manager,” and it refers to three distinct things at once: the command-line tool you run in a terminal, the public registry at registry.npmjs.org where packages are published, and the website that lets you browse and manage them .

The package manager solves a problem that becomes obvious the moment a project needs more than one library. A Node.js application might depend on a web framework, which depends on a router, which depends on a utility library, which depends on another utility library. Installing all of these by hand, in the correct versions, in the correct locations, is not practical. npm reads the project’s package.json file, resolves the full dependency tree, downloads every package, and places them in a node_modules directory where Node.js’s module resolution algorithm can find them . The result is a project that can be cloned, run with npm install, and behave identically on any machine.

Key point: npm is three things in one: a CLI for installing and managing packages, a public registry of JavaScript software, and a website for discovery. It installs packages into a node_modules directory by default, and it records the project’s direct dependencies in package.json and the full dependency tree in package-lock.json.


Why npm and node_modules exist

The dependency problem. A Node.js program that uses only the built-in modules is self-contained. The moment it needs a third-party libraryโ€”a web framework, a database driver, a testing toolโ€”it must obtain that library from somewhere and place it where Node.js can find it. npm is that “somewhere” and that “placement mechanism.” The npm install command reads the project’s declared dependencies, fetches them from the registry, and writes them into node_modules so that require('express') resolves to the installed package .

The version problem. Different packages often depend on different versions of the same library. Package A might require version 1.x of a utility, and package B might require version 2.x. A flat directory of packages cannot hold both versions at the same path. npm solves this with a nested node_modules structure: if a conflict exists, the conflicting version is installed inside the dependent package’s own node_modules folder, and Node.js’s module resolution walks up the directory tree until it finds the closest matching version . This is why node_modules can contain the same package at multiple paths, each with a different version.

The reproducibility problem. A package.json file declares the dependencies a project needs, often with version ranges like ^4.18.0. A range is not a specific version. On a fresh install months later, the range might resolve to a newer version that introduces a breaking change. The package-lock.json file solves this: it records the exact version and resolved location of every package in the tree, and npm install uses it to recreate the identical tree on every machine . This is why the lockfile is committed to version control and node_modules is not.

The size problem. node_modules is famously large because it contains not just the direct dependencies but every transitive dependency, recursively. A project with a dozen direct dependencies can easily have hundreds of packages in node_modules . The folder is not meant to be edited or committed. It is a build artifact, regenerated by npm install from the manifests. The .gitignore entry for node_modules is the standard practice because the lockfile is the source of truth, not the installed files .


a. The three components of npm

The CLI is the component most developers interact with. It is a command-line tool that runs in a terminal and exposes commands like install, publish, search, ls, and update . The CLI reads the project’s package.json, communicates with the registry, and writes to node_modules. It is installed automatically when Node.js is installed, and its version is shown with npm -v .

The registry is a large public database of JavaScript packages and their metadata. By default, npm uses the public registry at https://registry.npmjs.org . The registry stores the package tarballs and the metadata that describes them: the name, version, dependencies, and other fields. You can configure npm to use a different registry, including a private one, by changing the registry configuration option .

The website is the discovery and management interface. It lets you browse packages, read their documentation, and manage your account and organizations . The website and the registry are related but distinct: the website is for humans, and the registry is for the CLI. A package can exist in the registry without being prominently featured on the website, and the website can show information about a package that the CLI does not use directly.


b. Local versus global installation

npm has two modes of operation, and the distinction determines where a package is placed and what it can be used for.

Local mode is the default. Packages are installed into the node_modules directory of the current project, and their executables are linked into ./node_modules/.bin . A locally installed package is available to require() calls within the project and to the scripts defined in the project’s package.json. This is the correct mode for any library that the application’s code imports.

Global mode is activated with the -g or --global flag. Packages are installed into the Node.js installation prefixโ€”typically /usr/local/lib/node_modules on Unix systemsโ€”and their executables are linked into the prefix’s bin directory . A globally installed package is available as a command-line tool from any directory, but it is not available to require() calls in a project unless the project also installs it locally. The guidance is to install locally if the package is going to be require()d, and globally if it is going to be run from the command line .

ModeLocationExecutablesUse Case
Local (default)./node_modules./node_modules/.binLibraries imported by code
Global (-g){prefix}/lib/node_modules{prefix}/binCLI tools run from anywhere

The npm link command bridges the two modes for development. It creates a symbolic link from the global node_modules to the local package, so that a CLI tool under development can be run from anywhere without being published or reinstalled .


c. package.json and node_modules

The package.json file is the manifest. It contains the project’s metadata (name, version, description), its scripts (commands run with npm run), and its dependencies (the packages the project needs). It is the file that npm reads when you run npm install, and it is the file that you edit when you want to add or remove a dependency .

The node_modules directory is the installed output. When you run npm install, npm reads package.json, resolves the dependency tree, downloads the packages, and writes them into node_modules . The directory is not part of the source code. It is a cache and a build artifact, and it is regenerated from the manifest and the lockfile.

// A minimal package.json
{
  "name": "my-project",
  "version": "1.0.0",
  "description": "A sample project",
  "main": "index.js",
  "scripts": {
    // The test command, run with npm test
    "test": "node test.js"
  },
  "dependencies": {
    // The runtime dependency and its version range
    "express": "^4.18.0"
  },
  "devDependencies": {
    // The development-only dependency
    "jest": "^29.0.0"
  }
}

The dependencies field lists packages that the application needs at runtime. The devDependencies field lists packages that are only needed during development and testing, and they are skipped when npm install --omit=dev is used in a production build . The node_modules directory contains both sets after a normal npm install, which is why the production build uses the --omit=dev flag to exclude the development packages.


Complete Example Session

The following session creates a project, installs a dependency, and inspects the result.

# ============================================
# PART 1: CREATE THE PROJECT
# ============================================
# Create a directory and enter it
mkdir my-project && cd my-project

# Initialize a package.json with default values
# The -y flag skips the interactive prompts
npm init -y

# The package.json is created with the default fields
cat package.json

# ============================================
# PART 2: INSTALL A DEPENDENCY
# ============================================
# Install express locally
# npm fetches the package and its dependencies
# It writes the package to node_modules
npm install express

# The package.json now lists express in dependencies
grep express package.json

# The package-lock.json is created with the exact tree
ls package-lock.json

# ============================================
# PART 3: INSPECT THE RESULT
# ============================================
# The node_modules directory contains express and its dependencies
ls node_modules | head

# The express package is available to require()
node -e "const e = require('express'); console.log(typeof e)"

# ============================================
# PART 4: THE GLOBAL VS LOCAL DISTINCTION
# ============================================
# Install a CLI tool globally
npm install -g http-server

# The tool is available from any directory
which http-server

# The tool is not available to require() in the project
# because it was installed globally, not locally

The four parts cover the project creation, the dependency installation, the inspection of node_modules, and the global versus local distinction. What the session demonstrates is that npm install is the command that populates node_modules, and the package.json and package-lock.json are the files that make the installation reproducible.


Quick Reference

npm Components

ComponentPurpose
CLIInstall, publish, search, manage
RegistryPublic database of packages
WebsiteBrowse, discover, manage account

Installation Modes

ModeFlagLocation
Local(default)./node_modules
Global-g{prefix}/lib/node_modules

Key Files

FilePurpose
package.jsonManifest with metadata and dependencies
package-lock.jsonExact dependency tree for reproducibility
node_modulesInstalled packages (do not commit)

Dependency Types

FieldPurpose
dependenciesRuntime packages
devDependenciesDevelopment-only packages

Best Practices

โœ… Do This:

# Use local install for libraries
npm install express

# Use global install for CLI tools
npm install -g http-server

# Commit the lockfile
git add package-lock.json

# Ignore node_modules
echo "node_modules" >> .gitignore

โŒ Don’t Do This:

# Commit node_modules
git add node_modules # huge and unnecessary

# Install a library globally
npm install -g express # not available to require()

# Edit the lockfile by hand
vim package-lock.json # regenerated by npm

Common Pitfalls

PitfallWhy It HappensFix
require fails for a global packageGlobal packages are not in the project treeInstall locally
node_modules is hugeTransitive dependenciesNormal; do not commit
Install differs between machinesNo lockfileCommit package-lock.json
Permission errors on global installPrefix owned by rootUse a version manager

Real-World Examples

1. Install a dependency

npm install express

2. Install a dev dependency

npm install --save-dev jest

3. Install a CLI tool globally

npm install -g http-server

4. Initialize a project

npm init -y

5. List installed packages

npm ls

6. Search the registry

npm search express

7. Publish a package

npm publish

8. Run a script

npm run test

9. Link a local package

npm link

10. Check npm version

npm -v

Visual

The npm Workflow

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  package.json (dependencies)                 โ”‚
โ”‚         โ”‚                                    โ”‚
โ”‚         โ–ผ                                    โ”‚
โ”‚  npm install                                 โ”‚
โ”‚         โ”‚                                    โ”‚
โ”‚         โ–ผ                                    โ”‚
โ”‚  Registry (registry.npmjs.org)               โ”‚
โ”‚         โ”‚                                    โ”‚
โ”‚         โ–ผ                                    โ”‚
โ”‚  node_modules (installed tree)               โ”‚
โ”‚         โ”‚                                    โ”‚
โ”‚         โ–ผ                                    โ”‚
โ”‚  require('express') works                    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Local vs Global

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  LOCAL (default)                             โ”‚
โ”‚  โ†’ ./node_modules                            โ”‚
โ”‚  โ†’ require() works                           โ”‚
โ”‚  โ†’ ./node_modules/.bin for executables       โ”‚
โ”‚                                              โ”‚
โ”‚  GLOBAL (-g)                                 โ”‚
โ”‚  โ†’ {prefix}/lib/node_modules                 โ”‚
โ”‚  โ†’ require() does NOT work                   โ”‚
โ”‚  โ†’ {prefix}/bin for executables              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The Three Components

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  CLI          Registry        Website        โ”‚
โ”‚  โ”€โ”€โ”€โ”€         โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€        โ”€โ”€โ”€โ”€โ”€โ”€โ”€        โ”‚
โ”‚  install      packages        browse         โ”‚
โ”‚  publish      metadata        manage         โ”‚
โ”‚  search       tarballs        discover       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
npmPackage manager for Node.js
ComponentsCLI, registry, website
Default registryregistry.npmjs.org
Local install./node_modules
Global install{prefix}/lib/node_modules
Manifestpackage.json
Lockfilepackage-lock.json
Installed treenode_modules
Lockfile in VCSYes
node_modules in VCSNo

Key takeaways:

  • npm is three things in one. The CLI installs and manages packages, the registry stores them, and the website is for discovery and account management .
  • Local mode is the default. Packages go into ./node_modules, and they are available to require() calls in the project. Global mode is for CLI tools that need to be run from anywhere .
  • package.json declares dependencies, package-lock.json records the exact tree. The lockfile is what makes the install reproducible across machines .
  • node_modules is a build artifact. It contains every transitive dependency, it is large, and it is not meant to be committed to version control .
  • The registry is the source of packages. By default, npm uses the public registry, but it can be configured to use a private one .

Remember: npm is the tool that turns a list of dependencies in package.json into a working node_modules directory. The CLI is the interface, the registry is the source, and the lockfile is the guarantee of reproducibility. Install locally for libraries, globally for CLI tools, commit the lockfile, and ignore node_modules. That is the entire workflow.



Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!