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​