iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

npm Basics

npm is Node’s default package manager: install dependencies, run scripts, publish your own packages. Knowing the difference between install vs ci, dev vs prod deps, version ranges, and how the lockfile fits in covers 95% of daily use.

install/ci, scripts, lockfiles, versions

EXAMPLE
# 1) Anatomy of a package.json
{
    "name": "my-app",
    "version": "1.2.3",
    "type": "module",
    "main": "src/index.js",
    "scripts": {
        "dev":    "vite",
        "build":  "vite build",
        "start":  "node dist/server.js",
        "test":   "vitest",
        "lint":   "eslint .",
        "prepare": "husky install"
    },
    "dependencies": {
        "express": "^4.21.0",
        "zod":     "^3.23.0"
    },
    "devDependencies": {
        "vitest":  "^2.0.0",
        "eslint":  "^9.0.0",
        "typescript": "^5.5.0"
    },
    "engines": { "node": ">=20" },
    "private": true
}

# 2) Install
npm install                       # install everything from package.json + create/update lockfile
npm install express                 # add to dependencies (latest matching ^range)
npm install -D vitest               # add to devDependencies
npm install -g pnpm                 # global (use sparingly — prefer npx)
npm install express@4.21.2          # exact version
npm install express@latest          # update to latest
npm install gh:user/repo            # install from a GitHub repo

# 3) npm ci — the CI / reproducible install
npm ci                              # clean install from lockfile; deletes node_modules first
# - 2-5x faster than 'install' in CI
# - REFUSES to run if package.json and lockfile disagree
# - ALWAYS prefer over 'npm install' in CI scripts

# 4) Dependencies vs devDependencies vs peerDependencies
# dependencies          — runtime requirements (express, zod)
# devDependencies       — build/test only (vitest, eslint, typescript)
# peerDependencies      — your package expects the host to provide these (libraries you ship)
# optionalDependencies  — install if possible, ignore failure (native bindings)

# In production, install only what's needed:
NODE_ENV=production npm ci --omit=dev
# OR
npm ci --omit=dev

# 5) Version ranges — semver
# ^1.2.3   → >=1.2.3 <2.0.0     (compatible patches + minors)   ← npm default
# ~1.2.3   → >=1.2.3 <1.3.0     (compatible patches only)
#  1.2.3   → exactly 1.2.3      (no updates)
# 1.x      → >=1.0.0 <2.0.0
# *        → any version         (avoid)
# >=1 <3   → manual range
# latest   → resolves at install time (avoid in pinned deps)

# 6) Running scripts
npm run dev                         # runs scripts.dev
npm test                             # alias of 'npm run test' (also: start, restart, stop)
npm run build -- --mode=production   # forward args after --

# Lifecycle scripts — auto-run
# preinstall, install, postinstall — risky for security; prefer 'prepare'
# prepare           — runs after install; common for Husky hooks
# prepublishOnly    — runs before publish; build artifacts here

# 7) npx — execute a binary without installing globally
npx vitest                          # runs the local binary
npx -p typescript tsc --init        # one-shot from a registry package
npx create-react-app@latest my-app  # the modern pattern

# 8) Inspecting
npm ls                              # tree of installed packages
npm ls --depth=0                     # top-level only
npm outdated                         # what's newer than what you have
npm view express                     # registry info
npm view express versions --json     # all published versions
npm explain react-dom                 # why is this package in my tree?

# 9) Updating safely
npm update                          # update to highest version matching the range in package.json
npm install express@latest          # bumps the entry in package.json + lockfile
npx npm-check-updates -u            # interactive 'bump every dependency to latest' tool

# 10) Audit
npm audit                           # vulnerability scan of installed tree
npm audit --omit=dev                # production deps only
npm audit fix                       # auto-fix where possible (within current ranges)
npm audit fix --force                # bump major versions (read the diff first)

# 11) Lockfile — package-lock.json
# - Records EXACT versions + integrity hashes of every package in the tree
# - ALWAYS commit it
# - Resolves the same tree everywhere (npm ci respects it strictly)
# - On merge conflicts: regenerate via 'npm install' and commit

# 12) Workspaces (monorepo)
# Root package.json
{
    "workspaces": ["packages/*", "apps/*"],
    "scripts": {
        "build": "npm run build --workspaces --if-present"
    }
}

# Install for all workspaces at once
npm install

# Add a dep to one workspace
npm install -w packages/utils lodash

# Run a script in one workspace
npm run -w apps/web dev

# 13) Publishing your own package
npm whoami                           # confirm login
npm version patch                    # 1.2.3 -> 1.2.4 (also creates a git tag)
npm publish                          # public package
npm publish --access=public          # required for scoped packages on free accounts
npm publish --dry-run                # preview what will be uploaded

# 14) .npmrc — registry + behaviour config
# project .npmrc (commit) — affects this project
registry=https://registry.npmjs.org/
save-exact=true                       # writes exact versions (no ^/~) when adding deps
fund=false                            # silence fund messages
audit=true

# user ~/.npmrc — tokens, never commit
//registry.npmjs.org/:_authToken=...
@my-org:registry=https://npm.my-org.com

# 15) Common bugs
# • Committing node_modules — wastes git, conflicts on merge — keep in .gitignore
# • 'npm install' in CI instead of 'npm ci' — slow + may resolve different deps than locked
# • Editing package.json by hand and forgetting to run 'npm install' — lockfile drifts
# • Mixing npm + yarn + pnpm in one repo — three lockfiles, three resolutions, chaos
# • Storing tokens in .npmrc committed to git — instant supply chain risk
# • Using 'latest' as a version constraint — non-reproducible builds
# • Forgetting --omit=dev in Docker COPY → ships TypeScript + ESLint to production

Why it matters

Commit your lockfile, run npm ci in CI, and split runtime vs build-time deps with --save-dev. The ^ in version ranges means “patch and minor updates” — safe by default but breakable; pin exact versions with save-exact=true for libraries you publish, leave ranges in apps where lockfiles do the pinning.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
npm init -y
npm install express
npm install -D vitest
npm run dev
Try it Yourself »

Exercise

Install a dev dependency with…

npm install vitest

Discussion

Loading…