npm & package.json

Reviewed & published by Brayan K

npm is the default package manager for Node.js, and package.json is the manifest file at the root of every project that records its name, version, scripts, and the third-party dependencies npm installs.

Part of the free Node.js course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

By the end of this lesson you'll create a package.json with a single command, install both runtime and development packages, understand semver ranges, and define npm scripts that turn long commands into short, memorable names.

What You'll Learn in This Lesson

1️⃣ Creating a Manifest with npm init

Every Node.js project starts with a package.json file. You could write it by hand, but npm will scaffold one for you. Running npm init asks a series of questions; adding the -y ("yes") flag accepts all the defaults instantly.

# Make a new folder for your project and move into it.
mkdir my-app
cd my-app

# Create a package.json with sensible defaults — no questions asked.
# The -y flag answers "yes" to every prompt for you.
npm init -y

That file is the heart of your project. Here is an annotated example showing the fields you'll meet most often. The name, version, and main fields describe the package itself; scripts and the two dependency objects do the day-to-day work.

{
  "name": "my-app",            // the project's name (lowercase, no spaces)
  "version": "1.0.0",          // semantic version: MAJOR.MINOR.PATCH
  "description": "A demo app", // a short human-readable summary
  "main": "index.js",          // the entry file when someone requires your package
  "scripts": {                 // named shortcuts you run with "npm run <name>"
    "start": "node index.js"
  },
  "keywords": ["demo"],        // words that help people find your package
  "author": "Your Name",       // who made it
  "license": "MIT",            // how others may use it
  "dependencies": {},          // packages your app needs to RUN
  "devDependencies": {}        // packages you only need while DEVELOPING
}

2️⃣ Installing Packages

The whole point of npm is reusing code other people wrote. The npm install command (short: npm i) downloads a package and records it in your package.json. Where it gets recorded depends on the flag you use.

When npm writes a dependency, it usually adds a version range rather than a single exact version, so you can pick up small bug fixes automatically. Those ranges use semantic versioning (MAJOR.MINOR.PATCH) with two key symbols:

{
  "dependencies": {
    // ^ (caret) allows MINOR and PATCH updates: 4.18.2 -> up to <5.0.0
    "express": "^4.18.2",

    // ~ (tilde) allows only PATCH updates: 2.29.4 -> up to <2.30.0
    "dayjs": "~2.29.4",

    // No symbol = exact version. Only 1.4.0, nothing else.
    "left-pad": "1.4.0"
  }
}

3️⃣ npm Scripts

Typing long commands gets old fast. The "scripts" object lets you give a command a short name, then run it with npm run <name>. This is how almost every Node project standardises how it's started, developed, and tested.

{
  "name": "my-app",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",        // runs with: npm start
    "dev": "nodemon index.js",       // runs with: npm run dev
    "test": "jest"                   // runs with: npm test
  }
}

A few script names are special: start and test can be run without the word run. Every other script needs npm run in front of it.

# "start" and "test" are special — you can drop the word "run":
npm start          # same as: npm run start
npm test           # same as: npm run test

# Every OTHER script needs "run":
npm run dev

# List all available scripts in this project:
npm run

Your turn. Fill in the three blanks marked ___ to build a working manifest, then save it as package.json and run npm start.

{
  // 🎯 YOUR TURN — fill in each ___ then save as package.json.

  "name": "___",               // 👉 your project's name, lowercase (e.g. "todo-cli")
  "version": "1.0.0",
  "main": "___",               // 👉 the file Node runs first (e.g. "index.js")
  "scripts": {
    "start": "node ___"        // 👉 the SAME entry file from "main" above
  },
  "license": "MIT"
}

// ✅ Expected: running "npm start" launches your entry file.
//    > my-app@1.0.0 start
//    > node index.js

Mini-Challenge: Bootstrap a Project

No blanks this time — just a brief and an outline. Run the steps yourself in an empty folder and confirm the result against the expected file tree. This sequence is exactly how a real Node project begins.

# 🎯 MINI-CHALLENGE: bootstrap a real project from scratch
# Run these in order in an empty folder. No blanks — type them yourself.
#
# 1. Create the manifest with defaults
# 2. Install express as a runtime dependency
# 3. Install nodemon as a dev-only dependency
# 4. Add a "dev" script that runs:  nodemon index.js
# 5. Confirm node_modules and package-lock.json now exist
#
# ✅ Expected: package.json lists express under "dependencies",
#    nodemon under "devDependencies", and "npm run dev" works.

# your commands here

Common Errors (and the fix)

Pro Tips

📋 Quick Reference — npm Commands

TaskCommand
Create a manifestnpm init -y
Install a dependencynpm install pkg
Install a dev dependencynpm i -D pkg
Install everythingnpm install
Run a scriptnpm run name
Uninstall a packagenpm uninstall pkg
Install globallynpm i -g pkg

Tip: the "scripts" object lives inside package.json as { "start": "node index.js" }.

🎉 Lesson Complete!

Practice quiz

Which command creates a package.json with default values, no questions asked?

  • npm init -y
  • npm start
  • npm new
  • npm create

Answer: npm init -y. npm init -y answers yes to every prompt and writes a default package.json.

In semantic versioning, what does the format MAJOR.MINOR.PATCH mean for 1.4.2?

  • year.month.day
  • size.speed.fixes
  • major=1, minor=4, patch=2
  • patch.minor.major

Answer: major=1, minor=4, patch=2. 1.4.2 reads as MAJOR 1, MINOR 4, PATCH 2.

Which file lists the EXACT versions installed and SHOULD be committed?

  • node_modules
  • package-lock.json
  • README.md
  • .npmignore

Answer: package-lock.json. package-lock.json pins exact versions and belongs in version control.

Which folder holds the actual package code and should NEVER be committed?

  • dist
  • src
  • lib
  • node_modules

Answer: node_modules. node_modules holds installed code and is excluded from git.

What does the caret in "express": "^4.18.2" allow?

  • Only the exact 4.18.2
  • Minor and patch updates, up to <5.0.0
  • Any version including 5.x
  • Only major updates

Answer: Minor and patch updates, up to <5.0.0. Caret (^) permits minor and patch updates but not the next major.

What does the tilde in "dayjs": "~2.29.4" allow?

  • Major updates only
  • Any version at all
  • Patch updates only, up to <2.30.0
  • Nothing — it is exact

Answer: Patch updates only, up to <2.30.0. Tilde (~) permits only patch updates within the same minor version.

Which flag installs a package into devDependencies?

  • --prod
  • --global
  • --exact
  • --save-dev

Answer: --save-dev. npm install --save-dev (or -D) records the package under devDependencies.

Which command runs the script named "start" in package.json?

  • npm start
  • npm run-start
  • npm exec start
  • node start

Answer: npm start. start is special — npm start works without the word run.

How do you run a custom script named "dev"?

  • npm dev
  • npm run dev
  • node dev
  • npm exec dev

Answer: npm run dev. Every script other than start/test/etc. needs npm run <name>.

What does plain npm install (no package name) do in a cloned project?

  • Deletes node_modules
  • Publishes the package
  • Updates npm itself
  • Installs everything listed in package.json

Answer: Installs everything listed in package.json. Bare npm install installs all dependencies from package.json.

Continue this course

Frequently asked questions

What is the difference between dependencies and devDependencies?

dependencies are packages your app needs to run in production — like express for a web server. devDependencies are tools you only need while building and testing, such as nodemon, jest, or eslint. Install the first kind with npm install <pkg> and the second with npm install --save-dev <pkg>. When you deploy with npm install --production, npm skips devDependencies, giving you a smaller, faster install.

Should I commit node_modules to Git?

No. The node_modules folder can hold tens of thousands of files and is fully rebuildable from package.json and package-lock.json. Add node_modules to your .gitignore and commit package-lock.json instead — it records the exact versions so anyone who clones your repo gets an identical install by running npm install.

What do the ^ and ~ symbols mean in package.json versions?

They control which updates npm is allowed to install automatically. The caret ^ permits new minor and patch releases (^4.18.2 accepts anything below 5.0.0), while the tilde ~ permits only patch releases (~4.18.2 accepts anything below 4.19.0). A bare version like 4.18.2 pins that exact release. This is semantic versioning, written MAJOR.MINOR.PATCH.

What is package-lock.json and do I need it?

package-lock.json records the exact version of every package (and sub-dependency) that was installed, so installs are reproducible across machines and over time. You should commit it. While package.json may say ^4.18.2 (a range), the lock file nails down precisely 4.18.2, ensuring your teammates and your production server get byte-for-byte the same dependency tree.