npm & Package Management

📖 Concept

npm (Node Package Manager) is the world's largest software registry with over 2 million packages. It's both a CLI tool and a registry — essential for managing dependencies, scripts, and project configuration.

npm vs yarn vs pnpm:

Feature npm yarn pnpm
Speed Good (v7+) Fast Fastest
Disk usage Duplicated Duplicated Shared (content-addressable)
Lock file package-lock.json yarn.lock pnpm-lock.yaml
Workspaces ✅ v7+ ✅ v1+
Zero-install ✅ (PnP)
Strictness Loose Loose Strict (no phantom deps)

Essential npm commands:

npm init -y                    # Create package.json
npm install express            # Install to dependencies
npm install -D jest            # Install to devDependencies
npm install -g nodemon         # Install globally
npm uninstall express          # Remove a package
npm update                     # Update packages to latest allowed
npm outdated                   # Check for outdated packages
npm audit                      # Security vulnerability scan
npm audit fix                  # Auto-fix vulnerabilities
npm ls                         # List installed packages
npm ls --depth=0               # Top-level packages only
npm pack                       # Create a .tgz for distribution
npm publish                    # Publish to npm registry
npm run <script>               # Run a script from package.json
npx <command>                  # Run a package without installing

Semantic Versioning (semver):

MAJOR.MINOR.PATCH  →  2.4.1
  2 = Breaking changes
  4 = New features (backward compatible)
  1 = Bug fixes (backward compatible)

Version ranges in package.json:
  "^2.4.1"  → >=2.4.1 <3.0.0  (minor + patch updates)
  "~2.4.1"  → >=2.4.1 <2.5.0  (patch updates only)
  "2.4.1"   → Exactly 2.4.1   (locked)
  "*"       → Any version      (dangerous!)
  ">=2.0.0" → 2.0.0 or higher

package-lock.json: This file locks the exact versions of every dependency (including transitive deps). It ensures that npm install produces identical node_modules/ on every machine. Always commit it to version control.

🏠 Real-world analogy: npm is like an app store for code. package.json is your shopping list, package-lock.json is the receipt with exact product versions, and node_modules/ is your pantry where everything is stored.

💻 Code Example

codeTap to expand ⛶
1// npm & Package Management — Practical Usage
2
3// === package.json — The project manifest ===
4const packageJsonExample = {
5 "name": "my-node-app",
6 "version": "1.0.0",
7 "description": "A production-ready Node.js application",
8 "main": "src/index.js", // CJS entry point
9 "module": "src/index.mjs", // ESM entry point
10 "type": "module", // Treat .js as ESM
11
12 // Conditional exports (modern approach)
13 "exports": {
14 ".": {
15 "import": "./src/index.mjs",
16 "require": "./src/index.cjs"
17 },
18 "./utils": "./src/utils/index.js"
19 },
20
21 "scripts": {
22 "start": "node src/index.js",
23 "dev": "nodemon src/index.js",
24 "test": "jest --coverage",
25 "test:watch": "jest --watch",
26 "lint": "eslint src/",
27 "lint:fix": "eslint src/ --fix",
28 "format": "prettier --write 'src/**/*.js'",
29 "build": "tsc",
30 "precommit": "npm run lint && npm test",
31 "prepare": "husky install"
32 },
33
34 // Runtime dependencies
35 "dependencies": {
36 "express": "^4.18.2",
37 "mongoose": "^7.6.0",
38 "dotenv": "^16.3.1",
39 "helmet": "^7.1.0",
40 "cors": "^2.8.5",
41 "jsonwebtoken": "^9.0.2",
42 "bcryptjs": "^2.4.3",
43 "winston": "^3.11.0"
44 },
45
46 // Development-only dependencies
47 "devDependencies": {
48 "jest": "^29.7.0",
49 "supertest": "^6.3.3",
50 "nodemon": "^3.0.1",
51 "eslint": "^8.53.0",
52 "prettier": "^3.1.0",
53 "husky": "^8.0.3"
54 },
55
56 // npm config
57 "engines": {
58 "node": ">=18.0.0",
59 "npm": ">=9.0.0"
60 },
61
62 // Package metadata
63 "keywords": ["node", "api", "express"],
64 "author": "Your Name <you@example.com>",
65 "license": "MIT",
66 "repository": {
67 "type": "git",
68 "url": "https://github.com/you/my-node-app"
69 }
70};
71
72// === .npmrc — npm configuration ===
73// save-exact=true # Lock exact versions (no ^ or ~)
74// engine-strict=true # Enforce engine requirements
75// auto-install-peers=true # Auto-install peer dependencies
76
77// === npm scripts — Advanced patterns ===
78
79// Pre/post hooks — run automatically
80// "pretest": "npm run lint", ← runs before "test"
81// "test": "jest",
82// "posttest": "npm run report", ← runs after "test"
83
84// Script composition
85// "validate": "npm run lint && npm test && npm run build"
86// "dev": "concurrently 'npm run server' 'npm run client'"
87
88// Environment variables in scripts
89// "start:prod": "NODE_ENV=production node src/index.js"
90// Cross-platform (use cross-env):
91// "start:prod": "cross-env NODE_ENV=production node src/index.js"
92
93// === npx — Run without installing ===
94// npx create-react-app my-app # One-time project scaffolding
95// npx eslint --init # One-time setup
96// npx http-server ./public # Quick static server
97// npx tsx script.ts # Run TypeScript directly
98
99console.log("Package.json example created");
100console.log("Key sections: name, version, scripts, dependencies, devDependencies, engines");

🏋️ Practice Exercise

Exercises:

  1. Initialize a new Node.js project with npm init and configure all fields properly
  2. Add scripts for dev, test, lint, build, and start — use nodemon for dev mode
  3. Install 5 packages: 3 as dependencies and 2 as devDependencies — explain why each belongs in its category
  4. Use npm audit on a project — interpret the output and fix any vulnerabilities
  5. Create an .npmrc file with save-exact=true and engine-strict=true — explain the benefits
  6. Compare install times between npm, yarn, and pnpm for the same project

⚠️ Common Mistakes

  • Not committing package-lock.json to version control — this causes 'works on my machine' issues because dependency versions may differ

  • Installing dev tools as dependencies instead of devDependencies — jest, eslint, nodemon should be -D to keep production installs lean

  • Using npm install instead of npm ci in CI pipelines — npm ci uses the lockfile exactly, is faster, and fails if lockfile is outdated

  • Ignoring npm audit warnings — known vulnerabilities in dependencies are a real security risk in production

  • Using * or overly broad version ranges — this can pull in breaking changes on npm install; use ^ for libraries and exact versions for apps

💼 Interview Questions

🎤 Mock Interview

Mock interview is powered by AI for npm & Package Management. Login to unlock this feature.