03 - npm in Practice
📋 Jump to TakeawaysWhat npm Actually Is
npm is three things: a registry (where packages live), a CLI tool (what you type in the terminal), and a website (npmjs.com). When people say "npm" they usually mean the CLI.
It comes installed with Node.js. Check with:
npm --version
# 10.2.0package.json
Every Node.js project starts with this file. It describes your project and its dependencies.
npm init -yThis creates a basic package.json:
{
"name": "my-project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"type": "commonjs"
}Notice "type": "commonjs" at the bottom. That's the default. To use ES Modules (import/export), change it to "module":
{
"type": "module"
}The fields that matter most: name, type, scripts, dependencies, and devDependencies.
Installing Packages
# Add a dependency
npm install express
# or shorthand
npm i express
# Add a dev dependency (only needed during development)
npm install --save-dev vitest
npm i -D vitest
# Install everything from package.json
npm installAfter installing, your package.json gets updated:
{
"dependencies": {
"express": "^4.18.2"
},
"devDependencies": {
"vitest": "^1.2.0"
}
}Version Numbers
Versions follow semver: major.minor.patch
| Version | Meaning |
|---|---|
4.18.2 |
Exact version |
^4.18.2 |
Compatible with 4.x.x (minor + patch updates OK) |
~4.18.2 |
Patch updates only (4.18.x) |
* |
Any version (don't do this) |
The ^ prefix is the default. It allows non-breaking updates.
The Lockfile
package-lock.json pins the exact version of every dependency (and their dependencies). This ensures everyone on your team gets identical installs.
# Always commit package-lock.json
git add package-lock.jsonNever edit it manually. npm manages it for you.
Scripts
The scripts field in package.json lets you define commands.
{
"scripts": {
"dev": "vite dev",
"build": "vite build",
"lint": "eslint .",
"test": "vitest run",
"start": "node server.js"
}
}npm run dev # runs "vite dev"
npm run build # runs "vite build"
npm test # shorthand for "npm run test"
npm start # shorthand for "npm run start"test and start are built-in npm commands, so they don't need run. Everything else does. npm dev won't work, you need npm run dev. There's no deep reason for this, npm just has a small list of shorthand commands (test, start, stop, restart) and everything else requires run.
npx
Runs a package without installing it globally.
# The old way: install globally, then run
npm install -g create-vite
create-vite my-appWith npx, skip the global install entirely:
npx create-vite my-appThe first time, npx downloads the package and caches it. Next time, it uses the cache but checks npm for newer versions. That's why you sometimes see "Need to install the following packages... Ok to proceed?"
npx also runs binaries from your local node_modules:
# These are the same
./node_modules/.bin/vitest
npx vitestGlobal vs Local
# Local (project-specific)
npm install vitest
# Global (system-wide)
npm install -g vercelMost packages should be local. Each project gets its own version, no conflicts.
Some tools make sense globally because you use them across all projects or outside of projects entirely:
npm install -g vercel # Deploy from anywhere
npm install -g pm2 # Process manager for production
npm install -g nodemon # Auto-restart during developmentFor everything else, use local installs or npx. Don't install things like typescript or eslint globally. Different projects may need different versions.
Useful Commands
npm outdated # Show packages with newer versions
npm update # Update packages to latest allowed by ^ or ~ in package.json
npm ls # Show dependency tree
npm ls --depth=0 # Show only top-level dependencies
npm uninstall express # Remove a package
npm cache clean --force # Clear the global npm cache (~/.npm)Key Takeaways
npm init -ycreates a package.json, the starting point for any projectnpm installadds dependencies,npm i -Dadds dev dependencies- Versions use semver:
^allows minor updates,~allows patch only - Always commit
package-lock.json, it ensures consistent installs - Define commands in
scripts, run withnpm run <name> npxruns packages without global install- Prefer local installs over global to avoid version conflicts