← Node & Express

Modules & npm

What is the `package.json` for, and what do its most important fields like `scripts`, `main`, and `type` do?

Show answer — try answering out loud first

The package.json is the identity and configuration file of a Node project. It describes the project (name, version, description), states its entry file (main), which dependencies it needs (dependencies and devDependencies), which commands can be run (scripts, which you run with npm run <name>), and which module system it works in (type: "commonjs" or "module").

Think of the package.json as the project's "ID card". npm and Node read it to know what to install, how to start, and how to treat the code. The key fields:

  • name: the package's name. Lowercase, no spaces. If you published to npm, this would be its identifier.
  • version: the project's current version, in major.minor.patch format (semver), for example 1.4.2.
  • description: a short sentence explaining what the project is about.
  • main: the entry point. It's the file that gets loaded when someone does require('your-package'). It defaults to index.js.
  • scripts: an object with command shortcuts. Each key is a name and its value is the terminal command that runs. You run them with npm run <name> (for example npm run dev). Some names have their own shortcut: start and test can be run as npm start and npm test, without the run.
  • dependencies: the packages your app needs in production to work (for example express).
  • devDependencies: the packages you only use during development (for example nodemon, eslint, vitest). They aren't needed once the app is running on the server.
  • type: defines the default module system. With "commonjs" (or if you omit it) you use require/module.exports. With "module" you use import/export (ESM).

About scripts in more detail: when you run npm run dev, npm looks for the "dev" key inside scripts and runs that command in a shell, also adding the binaries from node_modules/.bin to the PATH. That's why you can write nodemon or eslint in a script even though they aren't installed globally.

A real package.json (remember: JSON doesn't allow comments, so below the JSON is clean and the explanation of each field goes separately).

{
  "name": "mi-api",
  "version": "1.0.0",
  "description": "API REST de ejemplo con Express",
  "main": "src/index.js",
  "type": "commonjs",
  "scripts": {
    "start": "node src/index.js",
    "dev": "nodemon src/index.js",
    "test": "vitest run",
    "lint": "eslint ."
  },
  "dependencies": {
    "express": "^4.19.0"
  },
  "devDependencies": {
    "nodemon": "^3.1.0",
    "vitest": "^1.6.0",
    "eslint": "^9.0.0"
  }
}

What each part of that file does:

  • name / version / description: identify the project and its current version.
  • main: doing require('mi-api') loads src/index.js.
  • type: "commonjs": the project uses require and module.exports.
  • scripts.start: npm start starts the app in normal mode with node.
  • scripts.dev: npm run dev starts with nodemon, which restarts the server when you save changes.
  • scripts.test: npm test runs the tests with Vitest.
  • scripts.lint: npm run lint checks the code style with ESLint.
  • dependencies: express is needed in production.
  • devDependencies: nodemon, vitest, and eslint are only used while developing.

How those commands look in the terminal:

npm start        # runs the "start" script (shortcut, no "run")
npm test         # runs the "test" script (shortcut, no "run")
npm run dev      # runs the "dev" script (custom names need "run")
npm run lint     # runs the "lint" script

Putting comments inside the package.json. JSON does not allow comments (// or /* */), and if you add them, npm will fail to read the file with a parse error. Another common mistake is trying to run a custom script without run, for example writing npm dev instead of npm run dev; only start, test, stop, and restart have a direct shortcut, everything else always needs run.

"The package.json is the project's configuration and identity file. It stores the name, the version, and the description, defines the entry point in main, and separates dependencies into dependencies, which are the production ones, and devDependencies, which are the development ones. In scripts I put command shortcuts that I run with npm run, like npm run dev to bring up the server with nodemon; start and test have their own shortcut and don't need the run. And the type field decides the module system: commonjs for require, or module for import and export. One important thing: JSON doesn't allow comments, so you should never put // inside the file."

Quick challenge

You have these scripts in your package.json. Which command runs each one correctly?

{
  "scripts": {
    "start": "node app.js",
    "seed": "node scripts/seed.js"
  }
}
See answer
  • start can be run as npm start (it has a shortcut) or also as npm run start.
  • seed is a custom name, so it needs run: npm run seed. Writing npm seed would error because npm doesn't recognize it as a command.