RizTech Academy logo
RizTech Academy
Modules and ToolingLesson 2 of 325 min

npm and package.json

Everything in this course so far has been written by you. That stops being sensible around the point you need date handling or a chart. npm is how you use somebody else's code — and how you take on somebody else's problems, which is the half that gets skipped.

Starting a project

npm init -y
{
  "name": "tiffin-tracker",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "license": "ISC",
  "type": "commonjs"
}

package.json is the project's description: what it is, what it needs, and how to run it. Change "type" to "module" so import works in Node, as the last lesson said.

Field Is
name, version Identity. version matters only if you publish
type "module" for ES modules, "commonjs" for require
main The entry point when someone imports your package
scripts Named commands
dependencies Needed to run
devDependencies Needed only to build or test

Installing

npm install dayjs
"dependencies": {
  "dayjs": "^1.11.23"
}

Three things happened: the package was downloaded into node_modules, recorded in package.json, and pinned exactly in package-lock.json.

For tools that never ship to users — formatters, test runners, bundlers:

npm install --save-dev prettier

The split matters because a deployment installs only dependencies with npm ci --omit=dev. Put a build tool in the wrong list and you ship it to production; put a runtime library in devDependencies and production breaks.

Version ranges

That ^ is not decoration.

Versions are MAJOR.MINOR.PATCH — semver:

Change Means
PATCH 1.11.23 → 1.11.24 A bug fix. Nothing else should change
MINOR 1.11.0 → 1.12.0 New features, still backwards compatible
MAJOR 1.0.0 → 2.0.0 Something that worked will stop working
Range Allows
^1.11.23 Any 1.x.x from 1.11.23. The default
~1.11.23 Any 1.11.x from 1.11.23
1.11.23 Exactly that
* Anything. Never do this

^ trusts the author to follow semver. Most do; some do not, and a "minor" release occasionally breaks things. That is what the lockfile is for.

package-lock.json

package.json says "any 1.x". package-lock.json says "exactly 1.11.23, and here are the exact versions of all seventy other packages that came with it".

Commit it. Without it, two developers running npm install a week apart get different code, and "works on my machine" becomes literally true.

Command Does
npm install Installs, and may update the lockfile
npm ci Installs exactly the lockfile. Deletes node_modules first

Use npm ci in CI and for deployments. It is faster and it cannot drift.

Never commit node_modules. Add it to .gitignore; it is rebuildable and enormous.

Scripts

"scripts": {
  "dev": "vite",
  "build": "vite build",
  "format": "prettier --write .",
  "test": "node --test"
}
npm run dev
npm test

npm run <name> for any script; test and start may drop the run.

Scripts can use installed tools by name — prettier resolves to node_modules/.bin/prettier, so you do not need it installed globally and everyone on the project gets the same version.

Scripts are the project's documented commands. A new person should be able to read scripts and know how to run everything, which is worth more than a README section that goes stale.

npx runs a package without installing it:

npx serve

Exactly what the last lesson used to serve a module page.

The cost of a dependency

The honest part, and the reason this lesson is not just a command list.

Installing two packages in an empty project — dayjs and express — gave me:

total installed packages: 70
direct dependencies: 2
node_modules: 15M

Two things I asked for, seventy things I got. That is normal, and each one is code you did not read, running with the same permissions as yours.

So, before adding a dependency:

Could you write it in twenty lines? A debounce, a slugify, a date formatter you use once — Intl from module 6 does most date and money formatting with nothing installed.

How many dependencies does it bring? Check on npm before installing.

Is it maintained? Last publish date, open issues, whether one person maintains it in their spare time.

What does it cost the user? A 200KB library on a mid-range Android on mobile data is a real delay. bundlephobia.com gives you the number.

And then, when you do need one — dates beyond Intl, charts, a framework — use it. Writing your own date library is a worse idea than any of this. The point is that it is a decision with a cost, not a free convenience.

Security

npm audit

Lists known vulnerabilities in your tree. npm audit fix updates what it safely can.

Two cautions, because this tool is widely over-trusted:

Not every report matters. A vulnerability in a build tool that never runs on a user's machine is not the same as one in your server. Read what it actually says.

npm audit fix --force installs major versions, which is a breaking change dressed up as a security fix. Never run it on a Friday.

The real risk is quieter: a package you trust is taken over and a malicious version published. Install scripts run automatically. That is why the lockfile matters, why npm ci is safer than npm install, and why adding a dependency for a twenty-line function is not free.

Where the browser fits

node_modules is for Node. A browser cannot import 'dayjs' — it has no idea what that means:

import dayjs from 'dayjs';
TypeError: Failed to resolve module specifier 'dayjs'

Browsers resolve paths, not package names. Bridging that is what a bundler — Vite, esbuild, webpack — does: it reads your imports, finds the packages, and produces files a browser can load.

Bundlers are beyond this course. You now know exactly what problem they solve, which makes learning one an afternoon rather than a mystery.

Check your work

npm init -y creates package.json. Set "type": "module" for import.

dependencies are needed to run; devDependencies only to build or test, and a deployment installs only the first.

^1.11.23 allows any 1.x from that version; ~ allows only patches; a bare version is exact. ^ is the default and trusts the author to follow semver.

package.json states a range; package-lock.json pins exact versions. Commit the lockfile. npm ci installs it exactly; npm install may change it.

Never commit node_modules.

Scripts can call installed tools by name, and are the project's documented commands. npx runs a package without installing it.

Two direct dependencies installed seventy packages and 15MB. Every one is unread code with your permissions.

npm audit fix --force installs major versions and can break your project.

A browser cannot resolve import 'dayjs' — it resolves paths, not package names. That is what a bundler is for.

Practice

  1. npm init -y in an empty folder, read every field, and switch it to "type": "module".
  2. Install a package and find the three places it now appears.
  3. Install something with --save-dev and confirm which list it lands in.
  4. Count the cost. In an empty project, install express and count the entries in package-lock.json and the size of node_modules.
  5. Delete node_modules and restore it with npm ci. Time both that and npm install.
  6. Change a version to ~ and work out which upgrades that now allows.
  7. Add a format script using a dev dependency and run it with npm run.
  8. Use npx serve to serve the module page from the last lesson.
  9. Run npm audit and read one report properly — what is vulnerable, and would it ever run on a user's machine?
  10. Try import 'dayjs' in a browser with type="module" and read the resolution error. That error is the entire reason bundlers exist.
  11. Harder. Pick a small package you might reach for — a debounce, a slugify, a currency formatter. Read its source on GitHub, count its dependencies, and then write your own version. Compare them, and decide honestly which you would ship. For at least one of the three, the answer should be your own.

Next: debugging — breakpoints instead of console.log, and reading what the browser is already telling you.

Stuck on this lesson?

Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.

About the internship