npm and package.json
You’ve used npm before to install packages for Vite projects. Now you’ll understand exactly what’s happening — because as a backend developer you’ll be managing packages directly rather than relying on scaffolding tools.
package.json — the project manifest
Section titled “package.json — the project manifest”Every Node.js project has a package.json at its root. It describes the project and records its dependencies:
{ "name": "bulletin-api", "version": "1.0.0", "description": "Bulletin community board API", "main": "dist/index.js", "scripts": { "dev": "tsx watch src/index.ts", "build": "tsc", "start": "node dist/index.js" }, "dependencies": { "express": "^4.18.2", "better-sqlite3": "^9.4.3", "bcrypt": "^5.1.1", "jsonwebtoken": "^9.0.2", "cors": "^2.8.5", "helmet": "^7.1.0", "dotenv": "^16.4.1" }, "devDependencies": { "typescript": "^5.4.2", "tsx": "^4.7.1", "@types/node": "^20.11.5", "@types/express": "^4.17.21", "@types/better-sqlite3": "^7.6.8", "@types/bcrypt": "^5.0.2", "@types/jsonwebtoken": "^9.0.5", "@types/cors": "^2.8.17" }}dependencies vs devDependencies
Section titled “dependencies vs devDependencies”dependencies are packages needed at runtime — the code that runs on the server in production. Express, bcrypt, SQLite go here.
devDependencies are only needed during development and building — TypeScript types, build tools, testing frameworks. When you deploy to Railway, Railway runs npm install --production and skips devDependencies.
Install to the right place:
npm install express # → dependenciesnpm install --save-dev typescript # → devDependenciesSemantic versioning
Section titled “Semantic versioning”Package versions follow MAJOR.MINOR.PATCH:
^4.18.2— caret: accept4.x.xupdates (same major, any minor/patch)~4.18.2— tilde: accept4.18.xupdates (same major.minor, any patch)4.18.2— exact: only this version
The ^ prefix is the npm default and what you’ll see most often.
package-lock.json
Section titled “package-lock.json”When you run npm install, npm creates package-lock.json — a file that records the exact version of every installed package (and their dependencies). Always commit package-lock.json to git. It ensures everyone on the team (and your production server) installs identical versions.
node_modules
Section titled “node_modules”Packages are installed into node_modules/. This directory can contain thousands of files and should never be committed to git. Add it to .gitignore:
node_modules/dist/.envWhoever clones your repo runs npm install to recreate node_modules from package-lock.json.
npm scripts
Section titled “npm scripts”Scripts in package.json are run with npm run <name>. They can chain commands:
{ "scripts": { "dev": "tsx watch src/index.ts", "build": "tsc --noEmit && tsc", "lint": "eslint src/", "clean": "rm -rf dist" }}npm run lists all available scripts. Scripts have access to locally-installed binaries in node_modules/.bin/ — that’s why tsx works without installing it globally.
Exercise
Section titled “Exercise”- In your
bulletin-apiproject, install all thedependencieslisted above in one command. - Install all the
devDependencieswith--save-dev. - Check your
package.jsonand confirm the packages went to the right section. - Open
node_modules/and note how many packages were installed (there will be many more than the 7 direct dependencies — they each have their own dependencies).
package.jsonrecords project metadata, scripts, and dependencies.dependenciesare needed at runtime;devDependenciesare only needed during development.- Semantic versioning:
^(same major),~(same major.minor), exact. - Always commit
package-lock.json; never commitnode_modules/. - npm scripts run with
npm run <name>and have access to local binaries.