Understanding package.json and npm

3 min read07 November 2026

package.json is the manifest npm and the platform read to know how to install and start your application. A sloppy file produces deployments that work on your machine and fail elsewhere. The genuinely useful fields are few, but each has a concrete effect.

The fields that matter

JSON
{
  "name": "my-app",
  "version": "1.4.0",
  "private": true,
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "build": "node build.js"
  },
  "engines": {
    "node": ">=20 <25"
  },
  "dependencies": {
    "express": "^5.1.0"
  },
  "devDependencies": {
    "eslint": "^9.0.0"
  }
}
  • name and version identify the project; private: true prevents accidental publication to the npm registry.
  • main points to the entry file; it must match the real file.
  • scripts holds named commands, run with npm run name or npm start.
  • engines documents the expected Node.js version; it is advisory unless engine-strict is enabled.
  • dependencies lists what is needed at runtime.
  • devDependencies lists development tools, absent in production.

Dependencies and versions

npm install express adds the library to dependencies and updates the lockfile. Version symbols control which updates are accepted:

Notation Accepts Use
^5.1.0 patches and minor 5.x default, common trade-off
~5.1.0 patches 5.1.x slower evolution
5.1.0 that exact version critical dependency

A dependency used only at build or test time goes in devDependencies (npm install --save-dev). If it is required at startup, it goes in dependencies: otherwise it will be missing in production.

Scripts and start command

Bash
npm start
npm run build
npm run

npm start runs the start script, or node server.js by default when the script is absent. The platform may also run node index.js directly: the script and the main file must therefore point to the same entry point.

The lockfile

npm install resolves version ranges and writes package-lock.json, which freezes the full dependency tree. Commit that file: without it, two installs can produce different versions.

Command Effect When to use
npm install updates package.json and the lockfile, may change versions development, adding a dependency
npm ci removes node_modules, installs exactly the lockfile deployment, continuous integration

npm ci fails when package.json and the lockfile are out of sync: that is a safeguard, not a bug. Fix the lockfile with npm install locally, then run it again.

Common errors

  • A module works locally because it is installed globally: declare it in dependencies.
  • npm ci fails without package-lock.json: commit the file.
  • A native module built on macOS does not run on Linux: reinstall it on the platform.
  • A build dependency is missing in production because it sits in devDependencies.
  • engines is not enforced: check the version selected in the panel and stay within a supported range.

A readable package.json and a committed lockfile are enough to make a Node.js deployment reproducible.

Advertisement

Was this article helpful?

Read next