TechLead
Lesson 11 of 16
5 min read
Node.js

npm & Package Management

Master package.json configuration, semantic versioning, lock files, npm workspaces for monorepos, publishing packages, and dependency auditing

npm Deep Dive

npm (Node Package Manager) is the world's largest software registry. Understanding how to manage dependencies, version packages, and organize monorepos is essential for professional Node.js development.

📦 npm Ecosystem

2M+ Packages

Largest open-source registry in the world

package.json

Project manifest: deps, scripts, metadata

Lock Files

Ensures reproducible installs across teams

package.json Deep Dive

{
  "name": "@myorg/api-server",
  "version": "2.1.0",
  "description": "Production REST API server",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "type": "module",
  "engines": {
    "node": ">=18.0.0"
  },
  "scripts": {
    "dev": "tsx watch src/index.ts",
    "build": "tsc",
    "start": "node dist/index.js",
    "test": "vitest",
    "test:coverage": "vitest --coverage",
    "lint": "eslint src/",
    "format": "prettier --write src/",
    "prepare": "husky install",
    "precommit": "lint-staged",
    "db:migrate": "prisma migrate dev",
    "db:seed": "tsx prisma/seed.ts"
  },
  "dependencies": {
    "express": "^4.18.0",
    "prisma": "^5.0.0",
    "zod": "^3.22.0"
  },
  "devDependencies": {
    "typescript": "^5.3.0",
    "vitest": "^1.0.0",
    "@types/express": "^4.17.0",
    "tsx": "^4.7.0"
  },
  "peerDependencies": {
    "react": ">=18.0.0"
  },
  "peerDependenciesMeta": {
    "react": { "optional": true }
  },
  "files": ["dist/", "README.md"],
  "repository": {
    "type": "git",
    "url": "https://github.com/myorg/api-server"
  },
  "license": "MIT"
}

Semantic Versioning (SemVer)

// Version format: MAJOR.MINOR.PATCH
// 2.1.0

// MAJOR: Breaking changes (2.0.0 -> 3.0.0)
// MINOR: New features, backward compatible (2.1.0 -> 2.2.0)
// PATCH: Bug fixes, backward compatible (2.1.0 -> 2.1.1)

// Version ranges in package.json:
// "^4.18.0"  -> >=4.18.0 <5.0.0  (most common, minor + patch updates)
// "~4.18.0"  -> >=4.18.0 <4.19.0 (patch updates only)
// "4.18.0"   -> exactly 4.18.0   (pinned)
// ">=4.18.0" -> 4.18.0 or higher
// "*"        -> any version

// Check outdated packages
// npm outdated

// Update within semver ranges
// npm update

// Update to latest (may include breaking changes)
// npx npm-check-updates -u && npm install

// View package info
// npm view express versions
// npm view express dependencies

Lock Files and Reproducible Builds

// package-lock.json locks EXACT versions of every dependency

// ALWAYS commit lock files to version control
// .gitignore should NOT include package-lock.json

// npm install  -> reads package.json, updates lock file
// npm ci       -> reads lock file only (faster, reproducible)

// In CI/CD pipelines, ALWAYS use npm ci:
// - Deletes node_modules first
// - Installs exact versions from lock file
// - Fails if lock file is out of sync with package.json

// Example CI step:
// npm ci --production  # Skip devDependencies in production

// Managing multiple package managers:
// corepack enable
// corepack prepare pnpm@latest --activate

// Compare: npm vs pnpm vs yarn
// npm  - default, widely used, flat node_modules
// pnpm - fastest, strict dependencies, content-addressable store
// yarn - plug'n'play, zero-installs, workspaces pioneer

Workspaces for Monorepos

// Root package.json
{
  "name": "my-monorepo",
  "private": true,
  "workspaces": [
    "packages/*",
    "apps/*"
  ]
}

// Structure:
// my-monorepo/
//   packages/
//     shared-utils/package.json    ("name": "@myorg/utils")
//     database/package.json        ("name": "@myorg/database")
//   apps/
//     web/package.json
//     api/package.json

// Install dependencies for all workspaces
// npm install

// Run scripts in specific workspace
// npm run build -w packages/shared-utils
// npm test -w apps/api

// Run scripts in all workspaces
// npm run build --workspaces

// Add dependency to specific workspace
// npm install lodash -w apps/api

// Cross-workspace dependencies
// In apps/api/package.json:
{
  "dependencies": {
    "@myorg/utils": "*",
    "@myorg/database": "*"
  }
}

Publishing Packages and npx

// Prepare for publishing
// 1. Set up package.json "files" field (whitelist)
// 2. Add .npmignore for anything not in "files"
// 3. Build before publishing

// Publish to npm
// npm login
// npm publish               # public package
// npm publish --access=public  # scoped public package

// Use a private registry
// .npmrc
// registry=https://npm.mycompany.com/
// //npm.mycompany.com/:_authToken=${NPM_TOKEN}

// Version bump and publish
// npm version patch  # 1.0.0 -> 1.0.1
// npm version minor  # 1.0.0 -> 1.1.0
// npm version major  # 1.0.0 -> 2.0.0
// npm publish

// npx: run packages without installing
// npx create-next-app my-app
// npx tsx script.ts
// npx -p @angular/cli ng new my-app

// Dependency auditing
// npm audit              # Check for vulnerabilities
// npm audit fix          # Auto-fix compatible issues
// npm audit fix --force  # Fix with breaking changes (careful!)

// Override vulnerable transitive dependencies
{
  "overrides": {
    "vulnerable-pkg": ">=2.0.1"
  }
}

💡 Key Takeaways

  • • Always commit lock files; use npm ci in CI/CD
  • • Understand SemVer: ^ allows minor updates, ~ allows patch updates
  • • Use workspaces for monorepo management
  • • Run npm audit regularly to find vulnerabilities
  • • Use npx to run CLI tools without global installs

Continue Learning