package.json
Your project's ID card - dependencies, scripts, and configuration all in one place.
The Project ID Card#
Every Node.js project has a package.json file. Think of it as your project's ID card - it tells everyone (and npm) what your project is, what it needs to run, and how to run it.
When you join a new team and clone their repo, the first thing you do is npm install. That command reads package.json and installs everything listed there. Without it, you'd have to manually track down every library the project needs.
Creating Your First package.json#
# Create a new project folder
mkdir my-api
cd my-api
# Initialize with prompts
npm init
# Or skip prompts with defaults
npm init -y
The -y flag accepts all defaults. You can always edit the file later.
Anatomy of package.json#
Here's a real-world example:
{
"name": "my-api",
"version": "1.0.0",
"description": "REST API for user management",
"main": "src/index.js",
"type": "module",
"scripts": {
"start": "node src/index.js",
"dev": "node --watch src/index.js",
"test": "node --test",
"lint": "eslint src/"
},
"dependencies": {
"express": "^4.18.2",
"mongoose": "^8.0.0",
"dotenv": "^16.3.1"
},
"devDependencies": {
"eslint": "^8.55.0"
},
"engines": {
"node": ">=20.0.0"
}
}
Let's break this down.
Essential Fields#
name and version#
{
"name": "my-api",
"version": "1.0.0"
}
The name must be lowercase, no spaces. Use hyphens for multi-word names.
Version follows semver (semantic versioning): MAJOR.MINOR.PATCH
- MAJOR: Breaking changes (1.0.0 → 2.0.0)
- MINOR: New features, backwards compatible (1.0.0 → 1.1.0)
- PATCH: Bug fixes (1.0.0 → 1.0.1)
main and type#
{
"main": "src/index.js",
"type": "module"
}
main is your entry point - the file that runs when someone imports your package or runs node .
type: "module" enables ES modules (import/export syntax). Without it, Node.js defaults to CommonJS (require/module.exports).
// With "type": "module"
import express from 'express';
// Without it (CommonJS)
const express = require('express');
Use ES Modules
Modern Node.js projects use ES modules. Set "type": "module" in new projects - it's the JavaScript standard and works better with modern tooling.
Scripts: Your Command Center#
Scripts are custom commands you run with npm run <name>:
{
"scripts": {
"start": "node src/index.js",
"dev": "node --watch src/index.js",
"build": "tsc",
"test": "node --test",
"lint": "eslint src/",
"lint:fix": "eslint src/ --fix",
"db:seed": "node scripts/seed.js",
"db:migrate": "node scripts/migrate.js"
}
}
Common Scripts#
| Script | Purpose | When to Use |
|---|---|---|
start | Run production server | Deployment, production |
dev | Run with auto-reload | Local development |
build | Compile TypeScript/bundle | Before deployment |
test | Run tests | CI/CD, before commits |
lint | Check code style | CI/CD, before commits |
Special Script Names#
start and test are special - you can run them without run:
npm start # Same as: npm run start
npm test # Same as: npm run test
npm run dev # Others need 'run'
npm run lint
Script Chaining#
Run multiple commands in sequence:
{
"scripts": {
"build": "npm run lint && npm run test && tsc",
"prestart": "npm run build",
"start": "node dist/index.js"
}
}
pre and post prefixes run automatically:
prestartruns beforestartposttestruns aftertest
Dependencies vs DevDependencies#
This distinction matters for deployment:
{
"dependencies": {
"express": "^4.18.2",
"mongoose": "^8.0.0"
},
"devDependencies": {
"eslint": "^8.55.0",
"typescript": "^5.3.0"
}
}
dependencies: Required to RUN your app
- Express (web framework)
- Mongoose (database)
- dotenv (config)
devDependencies: Only needed during DEVELOPMENT
- ESLint (linting)
- TypeScript (compiling)
- Jest (testing)
# Add to dependencies
npm install express
# Add to devDependencies
npm install -D eslint
npm install --save-dev typescript
Production Installs
When deploying, npm install --production (or npm ci --production) skips devDependencies. This makes your deployment smaller and faster.
Version Ranges#
Those ^ and ~ symbols control which versions npm can install:
{
"dependencies": {
"express": "^4.18.2", // ^: Minor updates OK (4.18.2 to 4.x.x)
"lodash": "~4.17.21", // ~: Patch updates only (4.17.21 to 4.17.x)
"uuid": "9.0.0" // Exact: Only this version
}
}
| Symbol | Meaning | Example: "^4.18.2" |
|---|---|---|
^ | Compatible updates | 4.18.2 → 4.99.99 |
~ | Patch updates | 4.18.2 → 4.18.99 |
| none | Exact version | 4.18.2 only |
The Lock File#
package-lock.json records the EXACT versions installed. This ensures everyone on your team gets identical dependencies.
# Install exact versions from lock file (CI/CD)
npm ci
# Update lock file (development)
npm install
Commit the Lock File
Always commit package-lock.json. It prevents "works on my machine" bugs caused by different dependency versions.
Engine Requirements#
Specify which Node.js versions your project supports:
{
"engines": {
"node": ">=20.0.0",
"npm": ">=10.0.0"
}
}
This warns users (or fails CI) if they're using an incompatible version.
Practical Example#
Here's a complete package.json for a production API:
{
"name": "user-api",
"version": "1.0.0",
"description": "User management REST API",
"main": "src/index.js",
"type": "module",
"scripts": {
"start": "node src/index.js",
"dev": "node --watch --env-file=.env src/index.js",
"test": "node --test 'src/**/*.test.js'",
"lint": "eslint src/",
"lint:fix": "eslint src/ --fix",
"format": "prettier --write src/",
"db:seed": "node scripts/seed.js"
},
"dependencies": {
"express": "^4.18.2",
"mongoose": "^8.0.0",
"jsonwebtoken": "^9.0.2",
"bcrypt": "^5.1.1",
"zod": "^3.22.4",
"pino": "^8.17.0"
},
"devDependencies": {
"eslint": "^8.55.0",
"prettier": "^3.1.0"
},
"engines": {
"node": ">=20.0.0"
},
"keywords": ["api", "rest", "users"],
"author": "Your Name",
"license": "MIT"
}
Common Commands#
# Install all dependencies
npm install
# Install for production only
npm ci --production
# Add a dependency
npm install express
# Add a dev dependency
npm install -D eslint
# Remove a dependency
npm uninstall lodash
# Update all dependencies
npm update
# Check for outdated packages
npm outdated
# Check for security issues
npm audit
Key Takeaways#
package.jsonis your project manifest - it defines what your project is and needs- Use
type: "module"for modern ES module syntax - Scripts are your command center - standardize how to run, test, and build
- dependencies vs devDependencies - matters for production deployments
- Commit
package-lock.json- ensures consistent installs across machines - Use
npm ciin CI/CD - faster and more reliable thannpm install
Quick Start Template
Start new projects with: npm init -y, then add "type": "module" and your scripts. Build the dependency list as you add features.
Ready to level up your skills?
Explore more guides and tutorials to deepen your understanding and become a better developer.