Phase 1 — Foundations lesson-0005

npm & Package Management

npm is the world's largest software registry. Understanding it saves you from dependency hell.

What npm Actually Is

npm has two parts that often get confused:

  • npm CLI — a command-line tool that ships with Node.js. You use it to install, update, and manage packages.
  • npm Registry — a public database at registry.npmjs.org hosting over 2 million packages.
Alternatives
pnpm and Bun are faster alternatives to the npm CLI that use the same registry. Many teams prefer pnpm for its disk efficiency. The commands are near-identical — once you know npm, the others take 10 minutes to pick up.

package.json — Your Project's Identity

Every Node.js project has a package.json. It's the manifest — it declares your project's name, version, dependencies, and scripts.

{
  "name": "my-api",
  "version": "1.0.0",
  "description": "A REST API for my app",
  "main": "src/index.js",        // entry point
  "scripts": {
    "start": "node src/index.js",   // npm start
    "dev": "nodemon src/index.js",    // npm run dev
    "test": "jest",                 // npm test
    "lint": "eslint src/"           // npm run lint
  },
  "dependencies": {
    "express": "^4.18.2"           // needed in production
  },
  "devDependencies": {
    "nodemon": "^3.0.1",           // only needed during development
    "jest": "^29.6.0"
  }
}

Dependencies vs devDependencies

dependencies

Packages needed to run your app in production.

Express, Prisma, bcrypt, jsonwebtoken, zod — anything your server imports at runtime.

npm install express

devDependencies

Packages only needed during development — stripped out in production builds.

nodemon, jest, eslint, typescript — tooling, not runtime code.

npm install -D nodemon

Semantic Versioning (SemVer)

Every npm package version follows the MAJOR.MINOR.PATCH format:

4
MAJOR
Breaking changes. You may need to update your code.
18
MINOR
New features, backwards compatible. Safe to upgrade.
2
PATCH
Bug fixes only. Always safe to upgrade.

The prefix characters in package.json control what versions are allowed:

PrefixExampleMeaning
^^4.18.2Accept MINOR and PATCH updates. MAJOR stays at 4. (Default)
~~4.18.2Accept PATCH updates only. More conservative.
exact4.18.2Locked to this exact version. Maximum reproducibility.

The Lock File

package-lock.json records the exact version of every package (and every sub-dependency) that was installed. This guarantees every developer on your team — and your production server — installs identical versions.

Critical Rule
Always commit package-lock.json to git. Never commit node_modules/ — add it to .gitignore. Anyone cloning your repo runs npm ci to reproduce the exact locked environment.

Essential Commands

# Initialise a new project (creates package.json)
npm init -y

# Install a production dependency
npm install express

# Install a dev dependency
npm install -D nodemon

# Install all dependencies from package.json (for new clones)
npm install

# Install from lock file — exact versions, no updates (use in CI/production)
npm ci

# Remove a package
npm uninstall express

# Check for outdated packages
npm outdated

# Update packages (respects semver ranges in package.json)
npm update

# Audit for known security vulnerabilities
npm audit

# Run a script defined in package.json
npm run dev
npm test      # shorthand for 'npm run test'
npm start     # shorthand for 'npm run start'

node_modules Explained

When you npm install, packages are downloaded to node_modules/. This directory can be enormous (hundreds of MB) because each package also installs its own dependencies recursively.

📁 node_modules/never commit this

📄 package.jsonalways commit this

🔒 package-lock.jsonalways commit this

🧠 Check Your Understanding


Go Deeper

Primary source: npm docs — package.json

Also read: semver.org — the full spec in plain English.

Ask your teacher: "What does npm ci do differently than npm install?" or "Why does node_modules get so large?"