Node.js Part 3: npm and package.json: Installing and Managing Packages

Node.js Part 3 banner: npm and package.json, installing and managing packages
PART 3 Node.js Series · DevDojo npm and package.json Install and Manage Packages dependencies, version ranges, lock files and npm scripts npm registry3M+ packages $ npm install dayjs node_modules npmpackage manager package.json package-lock.json npm run dev Reuse great code instead of writing it all yourself devdojo.co.in · Beginner friendly · Works on Node.js 22, 24 and 26
Series: Node.js Part 3 of 30

What you should know first: read Node.js Part 1: What Is Node.js and Node.js Part 2: Modules in Node.js. You should know how to run a file with node file.js and how import / export work.

In Part 2 you split your own code into modules. But most code in a real Node.js app is not written by you. Date formatting, web frameworks, database drivers, testing tools: someone has already built them and shared them for free. npm and package.json are how you find, install and manage that shared code. In this part you will learn, step by step, what npm is, what every important line of package.json means, how to install and remove packages, what the strange ^ and ~ symbols in version numbers mean, why package-lock.json exists, and how to run your project with npm scripts.

Quick Summary
  • npm is the package manager that comes with Node.js. It downloads packages from the npm registry, a huge online library of free JavaScript code.
  • package.json is your project’s ID card: name, version, scripts and the list of packages your project needs.
  • npm install name adds a package to dependencies; npm install -D name adds a development-only tool to devDependencies.
  • Versions follow MAJOR.MINOR.PATCH. ^1.11.0 allows minor and patch updates; ~1.11.0 allows only patch updates.
  • package-lock.json records the exact versions installed. Commit it, and use npm ci on servers and in CI for identical installs.
  • npm scripts (npm start, npm run dev, npm test) give every project the same simple commands.
Full Node.js roadmap (30 parts)

Beginner

  1. What Is Node.js and How to Build Your First Node.js App
  2. Modules in Node.js: ES Modules, CommonJS and import/export
  3. npm and package.json: Installing and Managing Packages You are here
  4. Working with Files and Folders: the fs and path Modules Coming soon
  5. Asynchronous Node.js: Callbacks, Promises and async/await Coming soon
  6. The Event Loop Explained Simply Coming soon
  7. Events and EventEmitter: Building Event-Driven Code Coming soon
  8. Streams and Buffers: Handling Large Data Efficiently Coming soon
  9. Building a Web Server with the http Module Coming soon
  10. Mini Project: Build a Command-Line Tool with Node.js Coming soon

Intermediate

  1. Express.js Basics: Routes, Requests and Responses Coming soon
  2. Middleware in Express: How Requests Flow Coming soon
  3. Build a REST API with Express: Full CRUD Coming soon
  4. Validating Request Data with Zod Coming soon
  5. Environment Variables and Configuration (–env-file and .env) Coming soon
  6. Connecting Node.js to PostgreSQL Coming soon
  7. Using an ORM: Prisma vs Drizzle Coming soon
  8. MongoDB with Mongoose Coming soon
  9. Authentication: Password Hashing, Sessions and JWT Coming soon
  10. Error Handling and Logging with Pino Coming soon

Advanced

  1. Testing Node.js Apps with node:test and Supertest Coming soon
  2. TypeScript with Node.js: Running .ts Files Directly Coming soon
  3. File Uploads and Image Processing Coming soon
  4. Real-Time Apps with WebSockets and Socket.IO Coming soon
  5. Caching with Redis Coming soon
  6. Background Jobs and Queues with BullMQ Coming soon
  7. Worker Threads and Clustering for Performance Coming soon
  8. Security Best Practices for Node.js APIs Coming soon
  9. Debugging, Profiling and Performance Tuning Coming soon
  10. Final Project: Build and Deploy a Production-Ready Node.js API Coming soon

What is npm?

npm (short for Node Package Manager) is installed automatically with Node.js. The word “npm” is used for three connected things:

1. The registryA huge free online library at registry.npmjs.org with millions of packages that anyone can download.
2. The command-line toolThe npm command in your terminal. It installs, updates and removes packages and runs scripts.
3. The websitenpmjs.com, where you search packages, read their docs and check weekly downloads.

A package is simply a folder of code with its own package.json. When you install one, you can import it exactly like the modules you wrote in Part 2. Check that npm is ready:

Terminal — my-first-npm-app
# npm comes with Node.js, so both commands should print a version
$ node -v
v22.23.4
$ npm -v
10.9.9

Your numbers may be higher, and that is fine. Everything in this tutorial works with the npm version that ships with Node.js 22, 24 or 26.

Create a package.json file

Every Node.js project starts with a package.json file in its main folder. You can write it by hand, but the quickest way is npm init. The -y flag answers “yes” to every question and uses default values.

Terminal — my-first-npm-app
# create a new project folder and a package.json inside it
$ mkdir my-first-npm-app
$ cd my-first-npm-app
$ npm init -y
Wrote to /home/you/my-first-npm-app/package.json:
package.json — my-first-npm-app — Visual Studio Code
my-first-npm-app › package.json
{
  "name": "my-first-npm-app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
⎇ mainLn 12, Col 1Spaces: 2UTF-8LFJSON

This is the file npm init -y created. Hover a line to highlight it.

Two quick improvements you will want in almost every new project: switch to ES modules and add a description. You can edit the file in your editor or use the npm pkg command, which edits package.json safely for you:

Terminal — my-first-npm-app
$ npm pkg set type=module
$ npm pkg set description="My first app with npm packages"
$ npm pkg get type
"module"

package.json fields explained

You do not need to memorise every field. These are the ones you will use every week. Click the tabs to explore them:

name and version identify your project. The name must be lowercase with no spaces (use dashes). The version follows MAJOR.MINOR.PATCH, for example 1.0.0. description, keywords, author and license are extra information, mostly useful if you publish the package. "private": true stops you from publishing an app to npm by mistake.

"type": "module" tells Node.js that .js files in this project are ES modules, so you can use import and export (you learned this in Part 2). engines documents which Node.js versions your app supports, for example "node": ">=22". main is the entry file for people who import your package; for an app you can remove it.

scripts are named shortcuts for commands. "start": "node app.js" lets anyone run npm start without knowing which file to open. Scripts can use tools installed in node_modules/.bin without a global install.

dependencies are packages your app needs to run (for example dayjs, express). devDependencies are tools you need only while developing (test runners, linters, TypeScript). Each entry is a package name plus a version range such as "^1.11.23".

The full list of fields is in the official npm package.json documentation.

Installing your first npm package

Let us install dayjs, a tiny and popular library for working with dates. Run this inside your project folder:

Terminal — my-first-npm-app
$ npm install dayjs

added 1 package, and audited 2 packages in 459ms

found 0 vulnerabilities
npm registry registry.npmjs.org npm install dayjs node_modules/ the downloaded code package.json “dayjs”: “^1.11.23” package-lock.json exact version 1.11.23
One npm install command does three things: downloads the code into node_modules, adds the package to package.json, and writes the exact version to package-lock.json.

Now use the package. Because dayjs lives in node_modules, you import it by its package name, with no ./ and no file extension. That is how Node.js knows to look in node_modules instead of your own files.

app.js — my-first-npm-app — Visual Studio Code
my-first-npm-app › app.js
// app.js – use an npm package like any other module
import dayjs from "dayjs";

const launch = dayjs("2026-12-25");
const today = dayjs("2026-11-06");

console.log("Launch date:", launch.format("DD MMM YYYY"));
console.log("Days to go:", launch.diff(today, "day"));
console.log("One week later:", today.add(7, "day").format("dddd, D MMMM"));
TERMINALOUTPUTPROBLEMSReal output (Node.js 22, npm, Linux)
$ node app.js
Launch date: 25 Dec 2026
Days to go: 49
One week later: Friday, 13 November
⎇ mainLn 9, Col 1Spaces: 2UTF-8LFJavaScript

Click ▶ Run to see the real output.

Tip: npm install has a short form, npm i. Both do exactly the same thing. You can also install several packages at once: npm i dayjs zod.
Never edit or commit node_modules. It can contain thousands of files and is always re-created by npm install. Add it to your .gitignore file.

dependencies vs devDependencies

Some packages are part of your running app. Others only help you while you write code. npm keeps them in two separate lists:

dependenciesdevDependencies
Install commandnpm install dayjsnpm install -D semver (or --save-dev)
Used forCode your app needs to runTools for building, testing and checking code
Examplesexpress, dayjs, zod, pgtypescript, eslint, prettier, vitest
Installed in production?YesNo, when you use npm ci --omit=dev

For this tutorial we install semver (the same version-checking library that npm itself uses) as a dev dependency, because we only need it to experiment:

Terminal — my-first-npm-app
$ npm install -D semver
added 1 package, and audited 3 packages in 402ms

found 0 vulnerabilities

Version numbers and ranges (^ and ~)

Look at package.json after installing and you will see "dayjs": "^1.11.23". That little ^ is a version range. npm packages use semantic versioning (semver): MAJOR.MINOR.PATCH.

1.11.23 MAJOR MINOR PATCH breaking changes new features bug fixes ^1.11.0 caret accepts 1.11.23 and 1.12.0, not 2.0.0 ~1.11.0 tilde accepts 1.11.23, not 1.12.0
Semantic versioning: the caret ^ (npm’s default) allows new features and fixes; the tilde ~ allows only bug fixes.

Seeing is believing. This small script uses the semver package to check which versions each range accepts:

versions.js — my-first-npm-app — Visual Studio Code
my-first-npm-app › versions.js
// versions.js – see which versions a range accepts
import semver from "semver";

const ranges = ["^1.11.0", "~1.11.0", "1.11.0", ">=1.10.0 <2.0.0"];
const versions = ["1.11.0", "1.11.23", "1.12.0", "2.0.0"];

for (const range of ranges) {
  const ok = versions.filter((v) => semver.satisfies(v, range));
  console.log(range.padEnd(16), "->", ok.join(", "));
}
TERMINALOUTPUTPROBLEMSReal output (Node.js 22, npm, Linux)
$ npm run versions
> my-first-npm-app@1.0.0 versions
> node versions.js

^1.11.0          -> 1.11.0, 1.11.23, 1.12.0
~1.11.0          -> 1.11.0, 1.11.23
1.11.0           -> 1.11.0
>=1.10.0 <2.0.0  -> 1.11.0, 1.11.23, 1.12.0
⎇ mainLn 10, Col 1Spaces: 2UTF-8LFJavaScript

Click ▶ Run to see which versions every range allows (real output).

RangeMeaning in simple wordsWhen to use
^1.11.0Any 1.x.x version from 1.11.0 upDefault. Good for most packages.
~1.11.0Only 1.11.x bug-fix versionsWhen a package sometimes breaks things in minor updates.
1.11.0Exactly this versionWhen you need full control (npm i dayjs --save-exact).
* or latestAny version at allAvoid in real projects.
Careful with 0.x versions: for packages below 1.0.0, ^0.4.2 only allows 0.4.x, because in the 0.x stage every minor update may break things. Learn more at semver.org.

package-lock.json and npm ci

If package.json says ^1.11.23, then today you may get 1.11.23 and next month your teammate may get 1.12.0. To avoid “it works on my machine” bugs, npm writes package-lock.json: the exact version, download URL and integrity hash of every installed package, including the packages that your packages depend on.

package-lock.json — my-first-npm-app — Visual Studio Code
my-first-npm-app › package-lock.json
{
  "name": "my-first-npm-app",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "node_modules/dayjs": {
      "version": "1.11.23",
      "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.23.tgz",
      "integrity": "sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/...",
      "license": "MIT"
    },
    "node_modules/semver": {
      "version": "7.8.6",
      "dev": true,
      "license": "ISC"
    }
  }
}
⎇ mainLn 19, Col 1Spaces: 2UTF-8LFJSON

A shortened view of the real lock file. The integrity hash is cut for space.

  • Always commit package-lock.json to Git. Never edit it by hand.
  • npm install may update the lock file when package.json changes.
  • npm ci (“clean install”) deletes node_modules and installs exactly what the lock file says. It fails if the two files do not match. Use it in CI pipelines and on servers. Our GitHub Actions CI/CD for Node.js guide uses it in every workflow.
Terminal — my-first-npm-app
# fresh, exact install from package-lock.json
$ npm ci
added 2 packages, and audited 3 packages in 475ms

found 0 vulnerabilities
# production server: skip devDependencies
$ npm ci --omit=dev

npm scripts: start, dev and test

Instead of remembering long commands, put them in the "scripts" section of package.json. Here is our finished file with four scripts:

package.json — my-first-npm-app — Visual Studio Code
my-first-npm-app › package.json
{
  "name": "my-first-npm-app",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "test": "node --test",
    "start": "node app.js",
    "dev": "node --watch app.js",
    "versions": "node versions.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "type": "module",
  "dependencies": {
    "dayjs": "^1.11.23"
  },
  "devDependencies": {
    "semver": "^7.8.6"
  }
}
TERMINALOUTPUTPROBLEMSReal output (Node.js 22, npm, Linux)
$ npm start
> my-first-npm-app@1.0.0 start
> node app.js

Launch date: 25 Dec 2026
Days to go: 49
One week later: Friday, 13 November
⎇ mainLn 21, Col 1Spaces: 2UTF-8LFJSON

Click ▶ Run to run npm start (real output).

  • npm start and npm test are special: they work without the word run.
  • Every other script needs npm run: npm run dev, npm run versions.
  • node --watch app.js restarts your app every time you save a file. It is built into Node.js, so you no longer need nodemon for simple projects.
  • Run npm run with no name to list every script in the project.

You can also set scripts from the terminal with npm pkg set:

Terminal — my-first-npm-app
$ npm pkg set scripts.dev="node --watch app.js"
$ npm run
Lifecycle scripts included in my-first-npm-app@1.0.0:
  test
    node --test
  start
    node app.js
available via `npm run-script`:
  dev
    node --watch app.js
  versions
    node versions.js

Running tools with npx

npx also comes with npm. It runs a command from a package without installing it globally. If the package is already in your project, npx uses that copy; otherwise it downloads it to a cache and runs it. Here we use the semver command-line tool from our dev dependency:

Terminal — my-first-npm-app
# which of these versions match ^1.11.0?
$ npx semver -r "^1.11.0" 1.10.7 1.11.5 1.12.0 2.0.0
1.11.5
1.12.0
# create projects without global installs (asks before downloading)
$ npx create-vite@latest my-react-app
Tip: prefer npx and project dev dependencies over npm install -g. Global packages are shared by all projects on your computer, so two projects that need different versions will fight.

Update, remove and audit packages

Packages get new versions all the time. These commands keep your project healthy. Here an older project has dayjs pinned to 1.11.10:

Terminal — old-project
# what is out of date?
$ npm outdated
Package  Current   Wanted   Latest  Location            Depended by
dayjs    1.11.10  1.11.10  1.11.23  node_modules/dayjs  old
# update within your ranges (the "Wanted" column)
$ npm update
# jump to the newest version, even a new major
$ npm install dayjs@latest
# remove a package from node_modules and package.json
$ npm uninstall semver
# check for known security problems
$ npm audit
$ npm audit fix
# see what is installed
$ npm ls

Current is what you have, Wanted is the newest version allowed by your range, and Latest is the newest version on the registry. When Latest is a new major version, read the package changelog before upgrading.

Common errors and fixes

ErrorWhy it happensFix
npm error code ENOENT … Could not read package.jsonYou ran npm outside your project folder, or the project has no package.json yet.cd into the project folder, or run npm init -y.
ERR_MODULE_NOT_FOUND: Cannot find package ‘dayjs’The package is not installed in this project (or node_modules was deleted).Run npm install (or npm install dayjs) in the project folder.
npm error code E404 … is not in this registryTypo in the package name, or the package does not exist.Check the exact name on npmjs.com and install again.
npm error Missing script: "dev"There is no dev entry in "scripts".Run npm run to list scripts, then add the missing one.
npm ci fails: package.json and package-lock.json are not in syncSomeone changed package.json without updating the lock file.Run npm install locally and commit the updated lock file.
npm error code ERESOLVE unable to resolve dependency treeTwo packages want different versions of the same peer dependency.Update the older package first; use --legacy-peer-deps only as a last resort.
EACCES: permission denied on global installnpm is trying to write to a system folder.Use npx, or install Node.js with a version manager (nvm, fnm). Do not use sudo.

npm and package.json best practices

  1. Commit package.json and package-lock.json; ignore node_modules. Anyone can rebuild node_modules with one command.
  2. Use npm ci in CI and on servers. It is faster and gives exactly the versions you tested.
  3. Put tools in devDependencies. Production stays smaller, faster to install and safer.
  4. Check a package before installing it: weekly downloads, last publish date, open issues and licence on npmjs.com. Fewer, well-maintained packages mean fewer security problems.
  5. Watch for typos. Attackers publish look-alike names (for example expresss). Copy names from the official docs.
  6. Run npm audit and npm outdated regularly, and update in small steps with your tests running.
  7. Add "private": true and engines to app projects so nobody publishes them by mistake and everyone knows the Node.js version.

Practice task

Build a tiny “festival countdown” app using npm:

  1. Create a folder countdown, run npm init -y and npm pkg set type=module.
  2. Install dayjs as a dependency.
  3. Create app.js that prints how many days are left until Diwali, New Year and your birthday.
  4. Add scripts: start (node app.js) and dev (node --watch app.js).
  5. Delete node_modules, run npm ci, then npm start. It should still work, because the lock file remembers everything.
  6. Bonus: install semver as a dev dependency and use npx semver to test which versions ~1.11.0 accepts.

FAQ

What is the difference between npm and Node.js?

Node.js runs JavaScript on your computer or server. npm is a separate tool that ships with Node.js and manages the packages your code uses. You run code with node and manage packages with npm.

Should I commit package-lock.json to Git?

Yes, for apps always. It makes every install on every machine use exactly the same versions, so bugs cannot appear just because someone installed a day later.

What does the ^ symbol mean in package.json?

The caret allows updates that do not change the first non-zero number. ^1.11.0 allows any 1.x version from 1.11.0 up, but never 2.0.0.

What is the difference between npm install and npm ci?

npm install installs packages and may update the lock file. npm ci deletes node_modules and installs exactly what package-lock.json says, failing if it does not match package.json. Use install while developing and ci in pipelines and on servers.

Should I use npm, pnpm, Yarn or Bun?

Start with npm: it is built in and every tutorial supports it. pnpm, Yarn and Bun use the same package.json and are good choices later for faster installs or monorepos. Pick one per project and do not mix lock files.

Can I delete the node_modules folder?

Yes. It is safe to delete at any time. Run npm install or npm ci to get it back.

Next part: Node.js Part 4, Working with Files and Folders: the fs and path Modules. You will read and write files with fs/promises, build safe paths with path, list folders and handle file errors the right way.

Conclusion

You now know how npm and package.json work together. npm downloads packages from the registry into node_modules; package.json lists what your project needs and the scripts it runs; package-lock.json remembers the exact versions. You can tell dependencies from devDependencies, read version ranges like ^ and ~, use npm ci for reliable installs, run tools with npx and keep packages updated and safe. These skills are used in every Node.js project you will build in the rest of this series.

New posts every day on DevDojo

Follow the full Node.js series, from your first script to a production-ready API. Bookmark devdojo.co.in and come back for the next lesson.

Share