npm 13 ๐ Updating, Auditing, and Removing Packages (npm update, npm audit, npm uninstall)
Package maintenance is not a single command but a cycle. npm update advances dependencies within the semver ranges declared in package.json. npm audit inspects the dependency tree for known vulnerabilities and optionally applies fixes. npm uninstall removes a package and cleans up every reference to it. Each command modifies a different artifactโnode_modules, package-lock.json, or package.jsonโand understanding which artifact each one touches is what prevents the common mistake of running the wrong command and wondering why nothing changed.
This chapter covers the three commands, their scope and limits, and the flags that control their behavior. It examines why npm update does not cross major version boundaries, how npm audit fix differs from npm audit fix --force, and what npm uninstall leaves behind when --no-save is used.
Key point: npm update respects semver ranges in package.json. It will not install a major version that the declared range excludes. Moving to a new major version requires npm install package@latest or a tool like npm-check-updates.
Why these commands exist
The dependency drift problem. A project installed six months ago has dependencies that were current then and are outdated now. Patch releases contain bug fixes, minor releases contain new features, and leaving them uninstalled means missing both. npm update advances the installed versions to the highest that the declared semver ranges allow, keeping the project current without introducing breaking changes .
The vulnerability problem. A dependency can be safe at publication and vulnerable six months later when a CVE is filed against it. npm audit checks the installed tree against a vulnerability database and reports which packages are affected, at what severity, and what the fix requires . Without this check, vulnerable dependencies remain in production because no one is looking.
The stale reference problem. Deleting a package’s directory from node_modules is not uninstalling it. The package remains in package.json, the lockfile still pins it, and the next npm install restores it. npm uninstall exists to remove the package and its references in one operation, so that the intent to remove is reflected in every artifact that records the dependency .
The lockfile consistency problem. npm update modifies package-lock.json without modifying package.json by default. This is intentional: the lockfile records what is installed, and the manifest records what is allowed. When --save is added, the manifest is updated too. Knowing which file a command modifies determines whether the change persists across a fresh npm install .
a. npm update
npm update advances installed packages to the highest version that satisfies the semver range in package.json. It does not change the range itself by default. A dependency declared as ^1.1.1 will update to 1.2.2 if that is the latest version satisfying the caret range, but it will not update to 2.0.0 because the range excludes it .
// package.json โ the range stays as written unless --save is used
{
"dependencies": {
"dep1": "^1.1.1"
}
}
# Updates node_modules and package-lock.json within the ^1.1.1 range
npm update
# Also updates the range in package.json to the installed version
npm update --save
The distinction between caret and tilde ranges determines how far npm update can go. A caret range (^1.1.1) permits minor and patch updates within the same major version. A tilde range (~1.1.1) permits only patch updates. If the published versions include 1.2.2 as the latest and the range is ~1.1.1, npm update installs 1.1.2, not 1.2.2, because 1.2.2 falls outside the tilde range .
Subdependencies add another constraint. If two packages depend on different ranges of the same transitive dependency, npm update chooses the highest version that satisfies both. If no single version satisfies both, it may keep two copies rather than forcing an incompatible version .
Global packages are updated with -g. Global packages do not have a package.json range, so npm treats them as if they were declared with a caret range. This means a global package can be downgraded if its installed version is higher than the latest tag .
# Update all globally installed packages
npm update -g
b. npm audit
npm audit submits the dependency tree to the registry and receives a report of known vulnerabilities. Each advisory includes the package name, severity, vulnerable version range, and a remediation path. The command exits with a non-zero code if vulnerabilities are found, which makes it suitable for CI pipelines .
# Report vulnerabilities without changing anything
npm audit
# Fail only on moderate or higher severity
npm audit --audit-level=moderate
npm audit fix applies remediations automatically. It runs an install under the hood, updating vulnerable packages to versions that are not affected. If the fix requires a major version change that falls outside the declared range, npm audit fix will not apply it unless --force is added .
# Apply safe fixes within semver ranges
npm audit fix
# Apply fixes including major version changes (breaking)
npm audit fix --force
# Preview what fix would do without changing files
npm audit fix --dry-run
The --force flag is not a convenience; it is a risk decision. It allows npm to install versions that violate the semver ranges in package.json, which means the fix may introduce breaking changes. The command should be followed by a full test run, and the package.json changes it makes should be reviewed before committing .
The signatures subcommand verifies the integrity of downloaded packages against registry signatures. This is a separate check from vulnerability scanning; it answers whether the package you downloaded is the package the registry published, not whether that package has known vulnerabilities .
# Verify registry signatures for downloaded packages
npm audit signatures
c. npm uninstall
npm uninstall removes a package from node_modules, removes it from every dependency section in package.json where it appears, and updates package-lock.json. The removal is complete: the package is gone from the installed tree and from the manifest that would restore it on the next install .
# Remove a package and all references
npm uninstall lodash
# Remove a scoped package
npm uninstall @scope/package-name
# Remove a global package
npm uninstall -g typescript
The --no-save flag changes the scope of the operation. It removes the package from node_modules but leaves it in package.json and the lockfile. This is useful when a package is temporarily broken and needs to be reinstalled fresh, or when testing whether a package is actually required at runtime without committing the removal .
# Remove from node_modules only; package.json unchanged
npm uninstall lodash --no-save
The aliases remove, rm, r, and un all invoke the same command. They exist for convenience and produce identical results .
Complete Example Session
# ============================================
# PART 1: CHECK CURRENT STATE
# ============================================
# See what is installed and what is outdated
npm outdated
# Lists packages with current, wanted, and latest columns
# ============================================
# PART 2: UPDATE WITHIN SEMVER RANGES
# ============================================
# Advances installed versions to the highest allowed by package.json
npm update
# package.json ranges unchanged; lockfile and node_modules updated
# ============================================
# PART 3: UPDATE AND SAVE RANGES
# ============================================
# Also updates the ranges in package.json to the installed versions
npm update --save
# Now package.json reflects the newer versions
# ============================================
# PART 4: AUDIT THE TREE
# ============================================
# Report vulnerabilities without changing anything
npm audit
# Exit code is non-zero if vulnerabilities are found
# ============================================
# PART 5: APPLY SAFE FIXES
# ============================================
# Updates vulnerable packages within semver ranges
npm audit fix
# Review the changes before committing
# ============================================
# PART 6: PREVIEW FORCED FIXES
# ============================================
# See what a major-version fix would do
npm audit fix --force --dry-run
# ============================================
# PART 7: APPLY FORCED FIXES
# ============================================
# Allows major version changes; may break compatibility
npm audit fix --force
# Run the full test suite afterward
# ============================================
# PART 8: REMOVE A PACKAGE
# ============================================
# Removes from node_modules, package.json, and lockfile
npm uninstall lodash
# ============================================
# PART 9: REMOVE WITHOUT CHANGING MANIFEST
# ============================================
# Useful for reinstalling fresh without committing removal
npm uninstall lodash --no-save
# ============================================
# PART 10: VERIFY REMOVAL
# ============================================
# Confirm the package is gone from node_modules
ls node_modules | grep lodash
# No output means removal succeeded
The ten parts trace the maintenance cycle: inspect, update within ranges, save ranges, audit, fix safely, preview and apply forced fixes, remove, and verify. The critical distinction is between update and audit fix --forceโone stays within the declared contract, the other violates it deliberately.
Quick Reference
Command Scope
| Command | Modifies node_modules | Modifies Lockfile | Modifies package.json |
|---|---|---|---|
npm update | Yes | Yes | No |
npm update --save | Yes | Yes | Yes |
npm audit | No | No | No |
npm audit fix | Yes | Yes | No (within ranges) |
npm audit fix --force | Yes | Yes | May change ranges |
npm uninstall | Yes | Yes | Yes |
npm uninstall --no-save | Yes | No | No |
Semver Range Behavior
| Range | npm update Installs | Example |
|---|---|---|
^1.1.1 | Highest 1.x.x โฅ 1.1.1 | 1.2.2 |
~1.1.1 | Highest 1.1.x โฅ 1.1.1 | 1.1.2 |
^0.4.0 | Highest 0.4.x โฅ 0.4.0 | 0.4.1 |
^0.2.0 | Highest 0.2.x โฅ 0.2.0 | 0.2.0 |
Audit Flags
| Flag | Effect |
|---|---|
--audit-level=moderate | Fail only on moderate+ severity |
--dry-run | Preview without changes |
--force | Allow major version fixes |
--json | Machine-readable output |
--package-lock-only | Update lockfile without node_modules |
Best Practices
โ Do This:
# Check outdated packages before updating
npm outdated # โ
# Review audit report before applying fixes
npm audit # โ
# Test after forced fixes; breaking changes are possible
npm audit fix --force && npm test # โ
# Use --no-save when reinstalling a package fresh
npm uninstall broken-package --no-save && npm install broken-package # โ
# Commit lockfile changes after update
git add package-lock.json && git commit # โ
โ Don’t Do This:
# Expect update to cross major versions
npm update # does not install 2.0.0 if range is ^1.0.0 # โ
# Run audit fix --force without testing
npm audit fix --force # โ
# Delete node_modules directory to uninstall
rm -rf node_modules/lodash # โ (package.json unchanged)
# Ignore audit exit code in CI
npm audit || true # โ
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
update does not install latest | Semver range excludes it | Use npm install pkg@latest |
audit fix does nothing | Fix requires major version | Review report; use --force if acceptable |
| Package returns after reinstall | --no-save used; manifest unchanged | Use npm uninstall without --no-save |
| Global package downgraded | latest tag is lower than installed | Pin version explicitly |
| Audit fails in CI on dev deps | --omit=dev not set | Use npm audit --omit=dev |
Real-World Examples
1. Check Outdated Packages
npm outdated
2. Update Within Ranges
npm update
3. Update and Save Ranges
npm update --save
4. Audit for Vulnerabilities
npm audit
5. Fail CI on Moderate+
npm audit --audit-level=moderate
6. Safe Fix
npm audit fix
7. Forced Fix with Test
npm audit fix --force && npm test
8. Remove Package
npm uninstall lodash
9. Remove Without Manifest Change
npm uninstall lodash --no-save
10. Verify Removal
ls node_modules | grep lodash
Visual
Command Scope
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ COMMAND MODIFIES โ
โ โ
โ npm update node_modules + lockfile โ
โ package.json: NO โ
โ โ
โ npm update --save node_modules + lockfile โ
โ package.json: YES โ
โ โ
โ npm audit nothing (read-only report) โ
โ โ
โ npm audit fix node_modules + lockfile โ
โ package.json: within ranges โ
โ โ
โ npm audit fix --force may violate ranges โ
โ โ
โ npm uninstall node_modules + lockfile โ
โ package.json: YES โ
โ โ
โ npm uninstall --no-save node_modules only โ
โ โ
โ The manifest is the contract. The lockfile is what is โ
โ installed. The two diverge when --save/--no-save is used. โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Semver Range and Update
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ package.json: "dep1": "^1.1.1" โ
โ Published: 1.2.2 (latest), 1.1.2, 1.1.1 โ
โ โ
โ ^1.1.1 means >=1.1.1 <2.0.0 โ
โ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ npm update โโโโโโบ installs 1.2.2 โ
โ โ โ 1.2.2 satisfies range โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ
โ package.json: "dep1": "~1.1.1" โ
โ ~1.1.1 means >=1.1.1 <1.2.0 โ
โ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ npm update โโโโโโบ installs 1.1.2 โ
โ โ โ 1.2.2 violates range โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ
โ npm update never crosses a major version boundary. โ
โ The range in package.json is the ceiling. โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Audit Fix Decision
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ npm audit finds vulnerability โ
โ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ Can fix within โโโโโโบ YES: npm audit fix โ
โ โ semver ranges? โ Safe, no breaking changes โ
โ โโโโโโโโโโฌโโโโโโโโโ โ
โ โ NO โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ Requires major โโโโโโบ npm audit fix --force โ
โ โ version change? โ May break compatibility โ
โ โโโโโโโโโโฌโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ Run tests โ Forced fixes can introduce โ
โ โ Review changes โ breaking changes โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ
โ --force is a risk decision, not a convenience. โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Uninstall Scope
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ npm uninstall lodash โ
โ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ node_modules/ โ Directory removed โ
โ โ lodash โ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ package.json โ "lodash" removed from dependencies โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ package-lock โ Entry removed โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ
โ npm uninstall lodash --no-save โ
โ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ node_modules/ โ Directory removed โ
โ โ lodash โ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ package.json โ "lodash" STILL PRESENT โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ package-lock โ Entry STILL PRESENT โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Next npm install restores lodash if --no-save was used. โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
npm update scope | Within semver ranges; lockfile modified |
npm update --save | Also modifies package.json ranges |
| Major version update | Requires npm install pkg@latest |
npm audit | Read-only vulnerability report |
npm audit fix | Safe fixes within ranges |
npm audit fix --force | Allows major version changes |
npm uninstall | Removes from node_modules, package.json, lockfile |
npm uninstall --no-save | Removes from node_modules only |
| CI audit gate | npm audit --audit-level=moderate |
Key takeaways:
npm updaterespects the semver contract. It installs the highest version that satisfies the range inpackage.json, not the highest version published. Crossing a major version requires a deliberate change to the manifest, not an update command .npm audit fixis safe by default;--forceis not. The default fix stays within declared ranges.--forceallows npm to violate those ranges to resolve vulnerabilities, which can introduce breaking changes. Test after using it .npm uninstallcleans every artifact. It removes the package fromnode_modules,package.json, and the lockfile.--no-savelimits removal tonode_modules, leaving the manifest intact so the package returns on the next install .- The lockfile is not the manifest.
npm updatewithout--savechanges what is installed without changing what is allowed. The next fresh install will revert to the range inpackage.jsonunless the lockfile is committed and used. - Audit in CI with a severity threshold.
npm audit --audit-level=moderatefails the build on moderate or higher vulnerabilities. Running audit without a threshold in CI produces noise that gets ignored, which defeats the purpose.
Remember: These three commands address three different phases of dependency maintenance. update advances versions within the boundaries the project has declared. audit checks whether the installed tree contains known vulnerabilities and offers fixes that may or may not respect those boundaries. uninstall removes a dependency and every record of it, unless --no-save intentionally leaves the record behind. The boundaries matter because they are the project’s defense against breaking changes. update stays inside them. audit fix stays inside them by default. --force is the deliberate exception, and it should be treated as such.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!