npm and package.json Explained — A Beginner's Complete Guide
Every professional JavaScript project you'll ever work on uses npm. It powers over 2 million packages and is downloaded billions of times every week. When you land a junior dev role and someone says 'just run npm install', you need to know exactly what's happening under the hood — not just how to type the command.
Before npm existed, sharing code between projects was painful. You'd copy-paste files manually, lose track of versions, and when a bug fix came out you'd have no way of knowing. npm solves all of that by giving every project a single source of truth: the package.json file. It tracks what external code your project depends on, what version of it you need, and even how to run your app.
By the end of this article you'll be able to create a project from scratch, install packages, understand every key field in package.json, and explain the difference between a dependency and a devDependency — the question that trips up a surprising number of candidates in junior dev interviews.
What npm Actually Is and Why It Was Built
npm stands for Node Package Manager. It ships automatically when you install Node.js, so if Node is on your machine, npm is already there.
The core job of npm is simple: let developers share and reuse code. That reusable chunk of code is called a 'package'. A package could be anything — a tool to format dates, a full web framework, a utility to send emails. Instead of writing all of that yourself, you pull in someone else's tested, maintained package.
npm has three parts that work together. First, there's the registry — a huge online database at registry.npmjs.org where packages live. Second, there's the CLI (command-line interface) — the npm commands you type in your terminal. Third, there's the website (npmjs.com) where you can search for and read about packages.
Think of the registry as the supermarket's warehouse, the CLI as your shopping trolley, and npmjs.com as the store's website where you browse before you go. You don't need to think about all three at once — most of the time you're just typing commands in the terminal and npm handles the rest.
# First, verify that Node.js is installed on your machine # This prints the version of Node you have node --version # Now verify npm is installed — it comes bundled with Node # This prints your npm version npm --version # If you see version numbers printed, you're good to go. # If you see 'command not found', visit nodejs.org and install Node first.
10.2.4
Creating Your First package.json — The Project's Identity Card
Every npm project needs a package.json file sitting at its root. This file is the heart of your project. It records your project's name, version, which packages it depends on, and useful scripts for running or testing your app. Without it, npm has no idea what your project needs.
You create it by running 'npm init' inside your project folder. npm will ask you a series of questions — project name, version, description, entry point, and so on. If you just want sensible defaults and don't want to answer every question, use 'npm init -y' (the -y flag means 'yes to everything').
The resulting file is plain JSON — just text. You can open it in any code editor and edit it by hand. That's one of the things beginners find surprising: it's not magic, it's just a config file you can read and change.
Every field in package.json has a specific meaning. The two most important are 'name' (what your package is called, lowercase, no spaces) and 'version' (which follows a three-number system called Semantic Versioning, or semver, like 1.0.0). We'll look at what those numbers mean in a moment.
# Step 1: Create a new folder for your project mkdir my-first-node-project # Step 2: Move into that folder cd my-first-node-project # Step 3: Create a package.json with all default values # The -y flag skips the questionnaire and accepts all defaults npm init -y # Step 4: Look at what was created cat package.json
{
"name": "my-first-node-project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
Installing Packages — Dependencies vs devDependencies
Now for the fun part: pulling in other people's code. When you run 'npm install
But not all packages are equal. Some packages your app needs to actually run in production — like express (a web server) or axios (for making HTTP requests). These go in 'dependencies'. Other packages are only needed while you're developing — like jest (a testing tool) or eslint (a code quality checker). Your end users will never need these. These go in 'devDependencies'.
To install a production dependency: 'npm install axios' To install a dev-only dependency: 'npm install jest --save-dev'
The '--save-dev' flag is the difference. Get this right and your production deployments only install what they actually need, which makes them faster and leaner.
Semantic versioning controls which version gets installed. The version number '1.4.2' means: major version 1, minor version 4, patch 2. A caret symbol (^1.4.2) means 'install 1.4.2 or any compatible minor/patch update'. A tilde (~1.4.2) means 'install 1.4.2 or any patch update only'. Most of the time you'll see the caret and that's fine for beginners.
// First, in your terminal run: // npm install axios // npm install chalk --save-dev // Now let's use axios — a package for making HTTP requests // This is a PRODUCTION dependency (your app needs it to run) const axios = require('axios'); // Fetch a public post from a free test API async function fetchBlogPost(postId) { try { // axios.get sends an HTTP GET request to the URL const response = await axios.get( `https://jsonplaceholder.typicode.com/posts/${postId}` ); // response.data contains the actual JSON returned by the API const post = response.data; console.log('Post Title:', post.title); console.log('Post Body:', post.body); } catch (error) { // If the request fails, catch and print the error console.error('Failed to fetch post:', error.message); } } // Fetch the first blog post fetchBlogPost(1);
Post Body: quia et suscipit
suscipit recusandae consequuntur expedita et cum
reprehenderit molestiae ut ut quas totam
nostrum rerum est autem sunt rem eveniet architecto
npm Scripts — Your Project's Command Centre
The 'scripts' field in package.json is one of the most underappreciated features for beginners. It lets you define shortcut commands for your project that anyone on the team can run without knowing the underlying tool or its exact flags.
For example, instead of everyone having to remember 'node --watch src/index.js', you define a 'start' script once and everyone just types 'npm start'. Instead of 'jest --coverage --verbose', you write a 'test' script.
There are two special script names: 'start' and 'test'. These run with 'npm start' and 'npm test'. Any other custom script name you invent runs with 'npm run
Scripts can also chain commands together using '&&' (run second command only if first succeeds) or '&' (run both simultaneously). This is how teams build, lint, and test their code in one command. It's also how CI/CD pipelines (automated deployment tools) know how to build your project on a server.
Think of scripts as the user manual for your project. A new developer clones your repo, reads the scripts section, and immediately knows how to start, test, build, and deploy the app — without asking you.
{
"name": "my-first-node-project",
"version": "1.0.0",
"description": "A demo project to learn npm and package.json",
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "node --watch index.js",
"test": "jest --coverage",
"lint": "eslint src/**/*.js",
"build:and:lint": "npm run lint && node index.js"
},
"dependencies": {
"axios": "^1.6.7"
},
"devDependencies": {
"chalk": "^5.3.0",
"eslint": "^8.56.0",
"jest": "^29.7.0"
},
"keywords": ["demo", "nodejs", "npm"],
"author": "Your Name <you@example.com>",
"license": "MIT"
}
# Executes: node index.js
# Output: whatever your index.js prints
# Running: npm run dev
# Executes: node --watch index.js
# Node watches for file changes and auto-restarts — great for development
# Running: npm test
# Executes: jest --coverage
# Jest finds and runs all test files and prints a coverage report
| Aspect | dependencies | devDependencies |
|---|---|---|
| Purpose | Code your app needs to actually run | Tools you need while building/testing |
| Examples | express, axios, mongoose, react | jest, eslint, typescript, webpack |
| Install command | npm install | npm install |
| Installed in production? | Yes — always | No — skipped with npm install --production |
| Appears in package.json under | "dependencies": {} | "devDependencies": {} |
| Risk if missing in prod | App crashes or won't start | No impact — prod doesn't need them |
🎯 Key Takeaways
- npm is a package manager — it lets you install, share, and manage reusable code. It installs automatically with Node.js, so you don't need to set it up separately.
- package.json is your project's identity card and shopping list — it records the project name, version, scripts, and every package the project depends on.
- dependencies are packages your app needs to run in production (like express or axios). devDependencies are tools you only need while building or testing (like jest or eslint). Getting this wrong wastes resources on production servers.
- npm scripts in package.json are shortcut commands for your whole team. 'npm start' and 'npm test' are built-in shortcuts; every other custom script runs with 'npm run
'.
⚠ Common Mistakes to Avoid
- ✕Mistake 1: Committing node_modules to Git — Your repo balloons to hundreds of megabytes and clone times become painful. The fix is a single line in .gitignore: add 'node_modules/' before your first commit. If you've already committed it, run 'git rm -r --cached node_modules' then commit again.
- ✕Mistake 2: Installing everything as a regular dependency instead of using --save-dev — Testing frameworks like Jest and build tools like Webpack end up in 'dependencies' instead of 'devDependencies'. Your production server downloads thousands of files it'll never use. The fix: always ask yourself 'does my running app need this, or only my development process?' If it's the latter, use --save-dev.
- ✕Mistake 3: Deleting package-lock.json because it 'looks confusing' — package-lock.json records the exact version of every single package (and sub-package) installed. Without it, two developers running 'npm install' a week apart can get different sub-dependency versions, causing 'works on my machine' bugs. Never delete it and always commit it to Git alongside package.json.
Interview Questions on This Topic
- QWhat is the difference between dependencies and devDependencies in package.json, and can you give a real-world example of each?
- QIf a colleague clones your Node.js repository and the node_modules folder isn't there, what command do they run and how does npm know what to install?
- QWhat does the caret symbol (^) mean in a version like ^1.4.2 inside package.json, and how is that different from specifying an exact version like 1.4.2 with no symbol?
Frequently Asked Questions
What is the difference between npm and Node.js?
Node.js is a runtime — it's the engine that lets you run JavaScript outside of a browser. npm is a package manager that comes bundled with Node.js. Think of Node.js as a car engine and npm as the mechanic's tool kit — one runs your code, the other helps you manage the parts you need to build it.
What is package-lock.json and do I need it?
package-lock.json is an automatically generated file that records the exact version of every package and sub-package installed in your project. You should always commit it to Git. It ensures that every developer on your team and every production server installs the exact same versions, preventing subtle 'works on my machine' bugs.
Can I edit package.json by hand or should I only use npm commands?
You can absolutely edit package.json by hand — it's just a plain text JSON file. Developers do it all the time to update scripts, add metadata, or tweak version ranges. After editing it manually, run 'npm install' to make sure your node_modules folder reflects the changes you made.
Written and reviewed by senior developers with real-world experience across enterprise, startup and open-source projects. Every article on TheCodeForge is written to be clear, accurate and genuinely useful — not just SEO filler.