Skip to main content

Module 33: npm and Package Management

npm (Node Package Manager) is the world's largest software registry. Learn to manage dependencies, publish packages, and follow best practices.


1. npm Basics

1.1 Getting Started

# Check npm version
npm --version
npm -v

# Initialize a new project
npm init
# Or use defaults
npm init -y

# Install packages
npm install lodash # Add to dependencies
npm install --save-dev jest # Add to devDependencies
npm install -g nodemon # Install globally

# Shortcuts
npm i lodash # install
npm i -D jest # --save-dev
npm i -g nodemon # --global

# Install specific version
npm install lodash@4.17.21
npm install lodash@^4.17.0 # Compatible version
npm install lodash@~4.17.0 # Patch updates only
npm install lodash@latest # Latest version

# Uninstall packages
npm uninstall lodash
npm uninstall -D jest
npm uninstall -g nodemon

# Update packages
npm update # Update all packages
npm update lodash # Update specific package
npm outdated # Check for outdated packages

1.2 package.json

{
"name": "my-app",
"version": "1.0.0",
"description": "My awesome application",
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js",
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"build": "webpack --mode production",
"lint": "eslint src/",
"format": "prettier --write \"src/**/*.js\""
},
"keywords": ["node", "api", "rest"],
"author": "Your Name <you@example.com>",
"license": "MIT",
"dependencies": {
"express": "^4.18.2",
"lodash": "^4.17.21"
},
"devDependencies": {
"jest": "^29.0.0",
"nodemon": "^2.0.20",
"eslint": "^8.0.0"
},
"engines": {
"node": ">=16.0.0",
"npm": ">=8.0.0"
},
"repository": {
"type": "git",
"url": "https://github.com/username/repo.git"
},
"bugs": {
"url": "https://github.com/username/repo/issues"
},
"homepage": "https://github.com/username/repo#readme"
}

1.3 Semantic Versioning (SemVer)

// Version format: MAJOR.MINOR.PATCH
// 1.2.3
// │ │ └─ Patch: Bug fixes (backwards compatible)
// │ └─── Minor: New features (backwards compatible)
// └───── Major: Breaking changes

// Version ranges
"express": "4.18.2" // Exact version
"express": "^4.18.2" // Compatible (^) - allows 4.x.x (not 5.0.0)
"express": "~4.18.2" // Approximately (~) - allows 4.18.x (not 4.19.0)
"express": "*" // Any version (not recommended)
"express": ">=4.0.0" // Greater than or equal
"express": "<5.0.0" // Less than
"express": "4.x" // Any 4.x.x version

// Multiple conditions
"express": ">=4.0.0 <5.0.0"

// Examples
"^1.2.3" // Allows: 1.2.3, 1.2.4, 1.3.0, 1.9.9 (not 2.0.0)
"~1.2.3" // Allows: 1.2.3, 1.2.4, 1.2.9 (not 1.3.0)
Version Ranges

^ (caret) is most common - allows minor and patch updates. ~ (tilde) is more conservative - only patch updates.


2. npm Scripts

2.1 Common Scripts

{
"scripts": {
// Development
"dev": "nodemon src/index.js",
"start": "node src/index.js",

// Testing
"test": "jest",
"test:unit": "jest --testPathPattern=unit",
"test:integration": "jest --testPathPattern=integration",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",

// Building
"build": "webpack --mode production",
"build:dev": "webpack --mode development",
"prebuild": "npm run clean", // Runs before build
"postbuild": "npm run copy-files", // Runs after build

// Linting & Formatting
"lint": "eslint src/",
"lint:fix": "eslint src/ --fix",
"format": "prettier --write \"src/**/*.{js,json,md}\"",
"format:check": "prettier --check \"src/**/*.{js,json,md}\"",

// Utilities
"clean": "rm -rf dist",
"deploy": "npm run build && npm run upload",

// Pre and post hooks
"pretest": "npm run lint",
"posttest": "npm run coverage"
}
}

2.2 Running Scripts

# Run scripts
npm run dev
npm run test
npm run build

# Special scripts (no 'run' needed)
npm start
npm test
npm stop

# Pass arguments to scripts
npm run test -- --watch
npm run build -- --mode production

# Run multiple scripts
npm run lint && npm run test # Sequential
npm run lint & npm run test # Parallel (use npm-run-all for better support)

# Using npm-run-all
npm install --save-dev npm-run-all

# In package.json
{
"scripts": {
"clean": "rm -rf dist",
"build:js": "webpack",
"build:css": "postcss src/styles.css -o dist/styles.css",
"build": "npm-run-all clean build:*" # Run all build:* scripts
}
}

# Silent mode
npm run test --silent

# View script code
npm run
npm run-script

2.3 Environment Variables

{
"scripts": {
"dev": "NODE_ENV=development node index.js",
"prod": "NODE_ENV=production node index.js"
}
}
// In your code
const env = process.env.NODE_ENV || 'development';
console.log(`Running in ${env} mode`);

// Cross-platform (Windows compatible)
// npm install --save-dev cross-env

{
"scripts": {
"dev": "cross-env NODE_ENV=development node index.js",
"prod": "cross-env NODE_ENV=production node index.js"
}
}

3. Package Management

3.1 package-lock.json

// package-lock.json ensures consistent installs
// - Locks exact versions of all dependencies
// - Includes nested dependencies
// - Should be committed to version control

// Example structure
{
"name": "my-app",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "my-app",
"version": "1.0.0",
"dependencies": {
"express": "^4.18.2"
}
},
"node_modules/express": {
"version": "4.18.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
"integrity": "sha512-...",
"dependencies": {
"accepts": "~1.3.8",
"body-parser": "1.20.1"
}
}
}
}

// Commands
npm ci # Clean install (uses package-lock.json, faster for CI)
npm install # Install with package.json (updates package-lock.json)

3.2 .npmrc Configuration

# .npmrc file (project or global)

# Registry
registry=https://registry.npmjs.org/

# Scoped packages
@myorg:registry=https://registry.myorg.com/

# Authentication
//registry.npmjs.org/:_authToken=${NPM_TOKEN}

# Save exact versions
save-exact=true

# Use package-lock.json
package-lock=true

# Engine strict
engine-strict=true

# Progress
progress=false

# Audit
audit=true
audit-level=moderate

# Global .npmrc location
# Windows: C:\Users\{username}\.npmrc
# macOS/Linux: ~/.npmrc

# Project .npmrc location
# {project-root}/.npmrc

3.3 node_modules

# Understanding node_modules
node_modules/
├── express/
│ ├── package.json
│ ├── index.js
│ └── node_modules/ # Nested dependencies
│ ├── body-parser/
│ └── cookie/
├── lodash/
└── jest/

# Clean install
rm -rf node_modules package-lock.json
npm install

# Or use npm ci (faster, cleaner for CI/CD)
npm ci

# Prune unused packages
npm prune

# Check for issues
npm doctor

# Find duplicate packages
npm dedupe
.gitignore

Always add node_modules/ to .gitignore. Never commit dependencies to git - use package.json and package-lock.json instead.


4. Publishing Packages

4.1 Creating a Package

// 1. Create package
mkdir my-awesome-package
cd my-awesome-package
npm init

// 2. Write code
// index.js
function greet(name) {
return `Hello, ${name}!`;
}

function add(a, b) {
return a + b;
}

module.exports = { greet, add };

// 3. Add README.md
// # My Awesome Package
//
// ## Installation
// ```bash
// npm install my-awesome-package
// ```
//
// ## Usage
// ```javascript
// const { greet } = require('my-awesome-package');
// console.log(greet('World'));
// ```

// 4. Configure package.json
{
"name": "my-awesome-package",
"version": "1.0.0",
"description": "An awesome package",
"main": "index.js",
"scripts": {
"test": "jest"
},
"keywords": ["awesome", "utility"],
"author": "Your Name",
"license": "MIT",
"files": [
"index.js",
"lib/",
"README.md"
],
"repository": {
"type": "git",
"url": "https://github.com/username/my-awesome-package"
}
}

// 5. Add .npmignore (what NOT to publish)
node_modules/
*.test.js
.env
.git
coverage/
.vscode/

// 6. Test locally
npm link # Create global symlink

// In another project
npm link my-awesome-package

4.2 Publishing to npm

# 1. Create npm account
npm signup

# 2. Login
npm login

# 3. Check what will be published
npm pack --dry-run

# 4. Publish
npm publish

# Publish scoped package (public)
npm publish --access public

# Update version 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

# Unpublish (within 72 hours only)
npm unpublish my-awesome-package@1.0.0

# Deprecate version
npm deprecate my-awesome-package@1.0.0 "This version has bugs, use 1.0.1"

4.3 Scoped Packages

// Scoped packages: @username/package-name or @org/package-name

// package.json
{
"name": "@myusername/my-package",
"version": "1.0.0"
}

// Publish
npm publish --access public

// Install
npm install @myusername/my-package

// Use
const pkg = require('@myusername/my-package');

// Private scoped packages (requires paid account)
npm publish // Default is private for scoped packages

5. npm Alternatives

5.1 Yarn

# Install Yarn
npm install -g yarn

# Initialize project
yarn init

# Install packages
yarn add lodash # Add dependency
yarn add --dev jest # Add dev dependency
yarn global add nodemon # Install globally

# Remove packages
yarn remove lodash

# Update packages
yarn upgrade
yarn upgrade lodash

# Install all dependencies
yarn install

# Run scripts
yarn start
yarn test
yarn run build

# yarn.lock (similar to package-lock.json)
# Always commit to version control

5.2 pnpm

# Install pnpm
npm install -g pnpm

# Initialize project
pnpm init

# Install packages
pnpm add lodash # Add dependency
pnpm add -D jest # Add dev dependency
pnpm add -g nodemon # Install globally

# Remove packages
pnpm remove lodash

# Update packages
pnpm update
pnpm update lodash

# Install all dependencies
pnpm install

# Run scripts
pnpm start
pnpm test
pnpm run build

# Benefits
# - Faster than npm and yarn
# - Disk space efficient (hard links)
# - Strict dependency resolution

5.3 Comparison

FeaturenpmYarnpnpm
SpeedMediumFastFastest
Disk SpaceMostMoreLeast
Lock Filepackage-lock.jsonyarn.lockpnpm-lock.yaml
WorkspacesYesYesYes
PnPNoYesNo
Market ShareHighestMediumGrowing

6. Best Practices

6.1 Security

# Audit packages for vulnerabilities
npm audit

# Fix vulnerabilities automatically
npm audit fix
npm audit fix --force # May include breaking changes

# Check for outdated packages
npm outdated

# Use security tools
npm install -g snyk
snyk test
snyk wizard

# Keep dependencies updated
npm update

# Use exact versions for critical packages
{
"dependencies": {
"express": "4.18.2" // No ^ or ~
}
}

6.2 Performance

// Use npm ci in CI/CD
// package.json
{
"scripts": {
"ci": "npm ci && npm run test"
}
}

// Leverage caching
// In .gitignore
node_modules/
package-lock.json # Don't ignore! Commit it

// In CI (GitHub Actions example)
- name: Cache dependencies
uses: actions/cache@v2
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}

// Optimize package size
{
"files": [
"dist/",
"README.md"
]
}

// Tree shaking (use ES modules)
// Use named exports
export function add(a, b) { return a + b; }

// Instead of
module.exports = { add };

6.3 Workspace and Monorepos

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

// packages/package-a/package.json
{
"name": "@myorg/package-a",
"version": "1.0.0",
"dependencies": {
"@myorg/package-b": "1.0.0"
}
}

// packages/package-b/package.json
{
"name": "@myorg/package-b",
"version": "1.0.0"
}

// Install all workspaces
npm install

// Run script in specific workspace
npm run test --workspace=@myorg/package-a

// Run script in all workspaces
npm run test --workspaces

7. Common Packages

7.1 Essential Packages

// Utility libraries
"lodash": "^4.17.21" // Utility functions
"date-fns": "^2.29.3" // Date manipulation
"uuid": "^9.0.0" // Generate UUIDs

// Web frameworks
"express": "^4.18.2" // Web framework
"fastify": "^4.0.0" // Fast web framework
"koa": "^2.14.1" // Next-gen web framework

// Database
"mongoose": "^6.0.0" // MongoDB ODM
"pg": "^8.8.0" // PostgreSQL client
"mysql2": "^3.0.0" // MySQL client

// Validation
"joi": "^17.7.0" // Schema validation
"validator": "^13.7.0" // String validators
"yup": "^0.32.11" // Schema validation

// Authentication
"jsonwebtoken": "^9.0.0" // JWT
"bcryptjs": "^2.4.3" // Password hashing
"passport": "^0.6.0" // Authentication middleware

// Testing
"jest": "^29.0.0" // Testing framework
"mocha": "^10.0.0" // Testing framework
"chai": "^4.3.7" // Assertion library
"supertest": "^6.3.0" // HTTP testing

// Build tools
"webpack": "^5.0.0" // Module bundler
"vite": "^4.0.0" // Build tool
"babel": "^7.0.0" // Transpiler

// Code quality
"eslint": "^8.0.0" // Linter
"prettier": "^2.8.0" // Code formatter
"husky": "^8.0.0" // Git hooks

// Environment
"dotenv": "^16.0.3" // Environment variables
"cross-env": "^7.0.3" // Cross-platform env vars

Summary

In this module, you learned:

  • ✅ npm basics and commands
  • ✅ package.json configuration
  • ✅ Semantic versioning
  • ✅ npm scripts and automation
  • ✅ Publishing packages
  • ✅ Security and auditing
  • ✅ npm alternatives (Yarn, pnpm)
  • ✅ Best practices and common packages
Next Steps

In Module 34, you'll get an Introduction to TypeScript, adding type safety to your JavaScript.


Practice Exercises

  1. Create and publish your own npm package
  2. Set up a monorepo with workspaces
  3. Create custom npm scripts for your workflow
  4. Audit and fix vulnerabilities in a project
  5. Configure ESLint and Prettier via npm
  6. Build a CLI tool with npm scripts
  7. Set up automated testing with npm scripts
  8. Create a project template generator

Additional Resources