Lesson 2 of 20

Installing Node.js & Setup

Installing Node.js and npm

Installing Node.js installs two programs, not one. The first is node, the runtime that executes your JavaScript. The second is npm, the package manager that downloads libraries and runs the scripts you define in your project. They arrive together in a single installer, which is why almost nobody installs npm separately, and why npm --version is the quickest way to confirm the installation actually completed.

The download page at nodejs.org always offers you two builds, and the choice matters. LTS stands for Long Term Support: a release line that gets bug fixes and security patches for years and that every hosting provider and every popular library is tested against. Current is the newer line where fresh features land first, and where a package you depend on is far more likely to have a rough edge. For learning, for college projects, and for anything you deploy, take LTS. The newest version is not a prize.

There are two sensible ways to install it. The plain installer from nodejs.org is the right choice if this is your first time — download, run it, restart your terminal. The alternative is a version manager: nvm on Linux and macOS, nvm-windows on Windows. A version manager keeps several Node versions on the same machine and lets you switch between them with one command. That sounds unnecessary until the day you join an internship whose codebase needs an older version than the one your personal project uses, and reinstalling Node every time you switch tasks becomes intolerable.

After installing, close and reopen your terminal before testing anything. Installers add Node to your system PATH — the list of folders your terminal searches for commands — and a terminal window that was already open still has the old PATH loaded. "I installed it but node is not recognised" is, nine times out of ten, simply a terminal that has not been restarted.

Once node --version prints something, you have everything you need for the rest of this course. There is no separate server to run, no configuration file to write, and nothing to license. A file with a .js extension and the command node file.js is the entire toolchain.

Example
# Confirm both programs are installed
node --version      # prints something like v22.x.x
npm --version       # npm ships with Node, so this must work too

# Run a JavaScript file
node app.js

# Run a file and restart it automatically when you save
node --watch app.js

# The REPL: an interactive prompt for quick experiments
node
> 2 + 2
4
> [1,2,3].map(n => n * 2)
[ 2, 4, 6 ]
> .exit

# With nvm (Linux/macOS) — install and switch versions
nvm install --lts
nvm use --lts
nvm ls              # every version installed on this machine
  • nodejs.org — download the LTS build unless you have a specific reason not to
  • nvm / nvm-windows — keep several Node versions side by side and switch per project
  • npm — bundled with Node.js; if npm --version fails, the install did not finish properly
  • REPL — type node with no filename to get an interactive JavaScript prompt
  • node --watch app.js — modern Node restarts your file on save, so a separate watcher tool is optional
  • Restart your terminal after installing, or the new PATH will not be visible to it
Notes
  • The REPL is genuinely useful, not a toy. When you cannot remember whether Number('12abc') gives 12 or NaN, checking in the REPL takes four seconds and beats guessing inside a route handler that you then have to restart.

Creating a Project with package.json

Every Node.js project begins with a file called package.json. It is a plain JSON file sitting in the root of your project folder, and it answers three questions: what is this project called, what libraries does it need, and what commands can be run against it. You create it with npm init, which asks a few questions, or npm init -y, which skips the questions and fills in sensible defaults you can edit later.

Its most important job is recording dependencies. When you later run npm install express, npm downloads Express and writes a line into package.json saying that this project needs it. Anybody who clones your repository then runs a single npm install and gets the exact same set of libraries. Without that file there is no way for another person — or a deployment server, or you on a different laptop — to know what your project needs.

The scripts block is the second reason it matters. Instead of remembering that this project starts with node src/server.js while the last one started with node index.js, you define "start" once and everybody types npm start. Deployment platforms rely on that convention: most of them simply run npm start and expect your server to come up.

One field trips people up more than the rest. "main" only matters when your project is a library that other code imports; for an application it changes nothing, and the file that actually runs is whichever one your start script names. Beginners often edit "main", restart, and are puzzled that nothing changed. Edit the script, not main.

Treat package.json as a real source file. It belongs in git, you will edit it by hand often, and a stray comma that makes it invalid JSON will stop every npm command with a parse error rather than anything that mentions your code.

Example
# Create a new project directory
mkdir my-node-app
cd my-node-app

# Initialize with interactive prompts
npm init

# Or skip prompts with defaults
npm init -y

# The generated package.json looks like:
{
  "name": "my-node-app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "dev": "node --watch index.js",
    "test": "echo \"No tests\" && exit 1"
  },
  "keywords": [],
  "license": "ISC"
}
  • npm init — interactive setup, asks name, version, entry point and licence
  • npm init -y — accept every default; the fastest way to start
  • scripts — named commands you run with npm run <name>
  • npm start and npm test — the two scripts that do not need the word run
  • dependencies — libraries your running application needs
  • devDependencies — tools only needed while developing, such as a test runner or a linter
Notes
  • Modern Node.js includes a --watch flag that restarts your program whenever a source file changes, so a "dev": "node --watch index.js" script often removes the need for a separate watcher package.

The Files Every Node Project Should Have

A new project folder needs more than package.json before it is safe to push anywhere. Three files in particular save real trouble, and all three are usually missing from a student's first repository.

The first is .gitignore. Its job is to list the things git must never record. Top of that list is node_modules/ — the folder npm downloads your libraries into. It can hold tens of thousands of files, it is completely reproducible from package.json, and committing it turns a two-second clone into a several-minute one. Right behind it is .env, the file where you will keep your database password and API keys. A secret committed to git is a secret forever, because it stays in the repository's history even after you delete the file in a later commit.

The second is .env.example, and it exists precisely because .env is hidden from git. It lists the names of every variable your project needs with the values left blank or obviously fake. It is committed, so a teammate cloning your repository can see at a glance which keys they must supply, instead of discovering them one crash at a time.

The third is package-lock.json, which npm generates for you the first time you install anything. Beginners frequently add it to .gitignore because it is long and machine-written. Do not. package.json records a range of acceptable versions; the lock file records the exact version of every package and every package those packages depend on. Committing it is what makes an install on your laptop and an install on the deployment server produce byte-identical code. Skip it and you invite the classic "but it works on my machine" bug, caused by a library that quietly released a new patch version between the two installs.

A README.md is worth adding too, even for a college project — three lines saying what the project is, how to install it, and how to run it. It is the first thing anyone opens, including the interviewer looking at your GitHub.

Example
my-node-app/
├── node_modules/       # downloaded libraries — NEVER commit
├── src/
│   └── index.js
├── .env                # real secrets  — NEVER commit
├── .env.example        # blank template — DO commit
├── .gitignore
├── package.json        # DO commit
├── package-lock.json   # DO commit
└── README.md

# .gitignore
node_modules/
.env
*.log
.DS_Store

# .env.example  (committed, no real values)
PORT=3000
DATABASE_URL=
JWT_SECRET=
  • node_modules/ — ignore it; it is rebuilt by npm install in seconds
  • .env — ignore it; it holds passwords, tokens and keys
  • .env.example — commit it; it documents which variables exist without leaking any
  • package-lock.json — commit it; it pins exact versions so every install matches
  • README.md — commit it; what the project is, how to install, how to run
Notes
  • Create .gitignore before your first commit, not after. Removing node_modules or a leaked .env from an existing history is far more painful than never adding them, and a key that has been pushed to a public repository must be treated as compromised and rotated, not just deleted.

Setting Up an Editor and a Workflow

Node.js does not require a heavy IDE. Almost everyone uses VS Code, which understands JavaScript out of the box and needs very little configuration to be productive. Two additions are worth the five minutes: a linter such as ESLint, which points out mistakes as you type rather than at runtime, and a formatter such as Prettier, so you stop arguing with yourself about indentation and stop producing diffs that are 90% whitespace.

The working rhythm for a Node project is short: open a terminal in the project folder, run your dev script, and leave it running in one window while you edit in another. With node --watch the process restarts on every save, so the loop is edit, save, glance at the terminal. When something crashes, the terminal is where the stack trace appears — keep it visible instead of buried behind the editor.

For testing an API you also want a way to send requests that is not the browser address bar, because a browser can only easily make GET requests. curl is installed nearly everywhere and is fine for quick checks. A graphical client such as Postman or the REST Client extension inside VS Code is more comfortable once your requests need headers and JSON bodies, which is roughly from lesson 12 onwards.

Finally, get into the habit of reading the first line of a stack trace instead of only the last. Node prints the error message first, then the chain of function calls that led to it, most recent first. The frames that mention files inside node_modules are rarely where your bug lives; scan down for the first line that names one of your own files.

Example
// package.json — a workable set of scripts to start with
{
  "name": "my-node-app",
  "version": "1.0.0",
  "scripts": {
    "start": "node src/index.js",
    "dev": "node --watch src/index.js",
    "lint": "eslint src/",
    "test": "node --test"
  }
}

# Day-to-day loop
npm run dev            # leave this running in one terminal

# In a second terminal, poke at your server
curl http://localhost:3000/api/health
curl -X POST http://localhost:3000/api/books \
  -H "Content-Type: application/json" \
  -d '{"title":"Dune","author":"Frank Herbert"}'
Notes
  • Keep the terminal running your server on screen while you work. Node reports unhandled errors and crashes there and nowhere else — if you hide that window, a server that died two minutes ago just looks like a browser that will not load.

Setup Problems and What They Actually Mean

Almost every installation problem in Node.js produces one of a small handful of messages, and each one has a specific cause. Learning to read them saves hours of random reinstalling.

'node' is not recognized on Windows, or command not found: node elsewhere, means the terminal cannot find the program. Either the install did not complete, or — far more often — the terminal was open before you installed and still holds the old PATH. Close every terminal window, open a fresh one, and try again before doing anything drastic.

EADDRINUSE when starting a server means something is already listening on that port. Usually it is an earlier copy of your own server that you forgot to stop, still running in a terminal tab you closed without pressing Ctrl+C. Either stop that process or start this one on a different port.

EACCES during a global install on Linux or macOS means npm is trying to write into a system folder your user cannot touch. The instinct is to prefix the command with sudo; resist it, because packages installed as the administrator create files your normal user then cannot manage, and the problem compounds. Installing Node through a version manager sidesteps the whole issue by keeping everything inside your home folder.

Cannot find module 'express' after you clearly installed Express usually means you are in the wrong folder — Node looks for node_modules starting from the file's own directory and walking upward, so a project installed one folder over will not be found. Check that package.json is in the directory you are running the command from, and run npm install again there.

  • node: command not found — restart the terminal so it picks up the new PATH
  • EADDRINUSE — an old server is still holding the port; stop it or use another port
  • EACCES on global install — do not reach for sudo; use a version manager instead
  • Cannot find module 'x' — wrong folder, or npm install was never run there
  • Unexpected token in package.json — a trailing comma or a missing quote in the JSON
  • Nothing happens when you save — the dev process is not running, or you are editing a different copy of the file
Notes
  • When an install goes badly wrong, the safe reset is to delete node_modules and run npm install again. It is completely non-destructive: everything in that folder is downloadable from package.json and package-lock.json. Deleting the lock file as well is a much bigger step and should be a last resort.
Ask AI