Node.js Get Started

Node.js is a runtime that lets you execute JavaScript outside a web browser, on your own machine or on a server. It is built on Google’s V8 engine (the same engine that powers Chrome) plus a C++ library called libuv that adds an event loop, a thread pool, and access to the file system, network, and other operating-system features. That combination is what lets a single-threaded JavaScript program handle thousands of concurrent connections without spawning a thread per request. In this lesson you will install Node.js, run your first scripts, understand what actually happens when you type node app.js, and build a tiny HTTP server.

Overview: What Node.js Actually Is

A normal JavaScript engine like V8 only understands the language itself — numbers, objects, functions, promises. It has no idea what a file or a network socket is, because browsers don’t let JavaScript touch those directly for security reasons. Node.js wraps V8 with a set of C/C++ bindings (via libuv) that expose the operating system: reading files, opening TCP sockets, spawning processes, timers, and more. On top of those bindings, Node ships a standard library written in JavaScript — modules like fs, http, path, and events — that you require or import into your own code.

The other core piece is the event loop. Node.js runs your JavaScript on a single main thread. When you call an asynchronous operation — reading a file, querying a database, waiting on a timer — Node hands that work off to libuv (which may use its own background thread pool for things like file I/O) and immediately continues running the rest of your script. When the operation finishes, libuv queues a callback, and the event loop picks it up and runs it once the main thread is free. This is why Node.js can serve many clients at once on one thread: it never sits idle waiting for slow I/O, it just processes whatever is ready next. You’ll go deeper into the event loop’s exact phases in a later lesson — for now, the important takeaway is that Node.js is asynchronous and single-threaded by default, and the code you write should avoid blocking that one thread.

Node.js also comes with npm (Node Package Manager), installed automatically alongside Node. npm lets you install reusable packages from the public registry and manages your project’s dependencies through a package.json file.

Installing Node.js

Download the current LTS (Long-Term Support) release from the official Node.js website, or install it through a version manager such as nvm (Node Version Manager), which makes it easy to switch between Node versions per project. LTS releases (even-numbered major versions like 20 and 22) are the ones you want for real projects — they receive security and bug fixes for years, unlike the short-lived odd-numbered “current” releases. This lesson targets Node.js 20 or 22 LTS.

Once installed, confirm it from a terminal:

node -v
# v22.11.0

npm -v
# 10.9.0

If both commands print a version number, you’re ready to go. If node is not found, your installer did not add Node to your system PATH — reinstall using the official installer for your OS, or use nvm, which sets this up for you automatically.

Syntax: Running Node.js

There are two main ways to run JavaScript with Node.js:

  • Run a file: node path/to/file.js executes the file top to bottom and exits when there’s no more work queued (no open server, no pending timer, etc).
  • The REPL: running node with no arguments drops you into a Read-Eval-Print Loop — an interactive prompt where you can type expressions and see results immediately. It’s useful for quickly testing a snippet, but not for real programs.
$ node
> 1 + 2
3
> console.log("hi")
hi
undefined
> .exit

The table below summarizes the commands you’ll use constantly while getting started:

Command What it does
node file.js Runs a script file with Node.js
node Opens the interactive REPL
node -v / node --version Prints the installed Node.js version
npm -v Prints the installed npm version
npm init -y Creates a default package.json in the current folder
npm install <package> Installs a package and adds it to package.json
npx <command> Runs a package’s CLI without installing it globally

Examples

Example 1: Your First Script

Create a file named hello.js and run it with node hello.js.

console.log("Hello from Node.js!");
console.log("Node version:", process.version);
console.log("Running on:", process.platform);

Output:

Hello from Node.js!
Node version: v22.11.0
Running on: linux

This introduces the global process object, which Node.js injects into every script (it’s not a module you import). process.version reports the exact Node build you’re running, and process.platform reports the operating system — useful when your code needs to behave differently across environments.

Example 2: Reading Command-Line Arguments

Node.js exposes everything typed after node on the command line through process.argv, an array. The first two entries are always the path to the Node executable and the path to your script, so any arguments you pass start at index 2.

// greet.js
const name = process.argv[2] ?? "stranger";
console.log(`Hello, ${name}!`);
console.log("All argv entries:", process.argv);

Run it with an argument:

node greet.js Maya

Output:

Hello, Maya!
All argv entries: [
  '/usr/bin/node',
  '/home/user/greet.js',
  'Maya'
]

The nullish coalescing operator (??) supplies a fallback only when process.argv[2] is null or undefined (i.e. no argument was passed), so running node greet.js with no name still prints a sensible greeting instead of “Hello, undefined!”.

Example 3: A Minimal HTTP Server

This is where Node.js starts to feel like a server platform rather than a scripting tool. The built-in node:http module lets you accept HTTP requests without installing anything.

const http = require("node:http");

const server = http.createServer((req, res) => {
  res.writeHead(200, { "Content-Type": "text/plain" });
  res.end("Hello from your first Node.js server!\n");
});

const PORT = process.env.PORT ?? 3000;

server.listen(PORT, () => {
  console.log(`Server running at http://localhost:${PORT}/`);
});

Output:

Server running at http://localhost:3000/

Save this as server.js and run node server.js. Unlike the previous two examples, this script does not exit — server.listen() keeps the event loop alive waiting for incoming connections, so Node.js stays running until you stop it with Ctrl+C or call server.close(). Visit http://localhost:3000/ in a browser (or run curl http://localhost:3000/) and you’ll see the response text. Notice the module is imported with the node: prefix (require("node:http")) — this is the current recommended style for built-in modules because it makes it unambiguous, at a glance, that http is a core module and not a package named http from npm.

How It Works Step by Step

When you run node server.js, here is the order of events:

  • Node.js starts, initializes the V8 engine and the libuv event loop, and loads your file as a CommonJS module.
  • Your script runs synchronously from top to bottom: the require("node:http") call loads the module, http.createServer() creates a server object (but does not yet listen for connections), and server.listen(PORT, callback) asks the OS to start listening on that port.
  • Once the synchronous script finishes, Node.js checks whether there’s anything left to do. Because the server is listening, there’s an active “handle” keeping the process alive, so Node.js does not exit — it enters the event loop and waits.
  • When a client connects, the OS notifies libuv, libuv notifies the event loop, and the event loop invokes your request handler callback — the function you passed to createServer() — with req and res objects for that specific request.
  • Inside the callback, res.writeHead() queues the status line and headers, and res.end() writes the body and tells Node the response is complete, flushing it back to the client.
  • The process repeats for every new connection, all on the same thread, until you terminate it.

Compare this to the two earlier examples: hello.js and greet.js have no pending timers, servers, or callbacks left after their last line runs, so the event loop finds nothing to wait for and Node.js exits immediately on its own.

Common Mistakes

Mistake 1: Mixing require and import, or getting the module type wrong

Node.js supports two module systems: CommonJS (require/module.exports) and ES Modules (import/export). Which one a file uses is determined by its extension and by the "type" field in the nearest package.json. If you set "type": "module" but write CommonJS-style code, Node.js throws at startup:

{
  "name": "my-app",
  "version": "1.0.0",
  "type": "module"
}
// app.js — fails because package.json says "type": "module"
const http = require("node:http");
console.log("starting...");

Running this prints ReferenceError: require is not defined in ES module scope, you can use import instead. Until you reach the lesson on ES Modules, keep new projects on the CommonJS default by leaving the "type" field out of package.json entirely (or setting it to "commonjs"), and stick to require/module.exports consistently:

{
  "name": "my-app",
  "version": "1.0.0"
}
// app.js — works: plain CommonJS, no "type" field set
const http = require("node:http");
console.log("starting...");

Mistake 2: Running code that requires an uninstalled package

Beginners often copy an example that does require("express"), run it, and hit Error: Cannot find module 'express'. Listing a dependency in package.json is not the same as having it installed — npm only writes files into node_modules when you actually run the install command:

npm install express

This downloads the package into a local node_modules folder and records it under "dependencies" in package.json. If you clone a project someone else wrote, always run npm install (no package name) first — it reads package.json and installs everything listed there before you try to run the code.

Best Practices

  • Install Node.js via nvm (or your OS’s official installer) and always use an even-numbered LTS release for real projects, not the latest “current” release.
  • Run npm init -y at the start of every new project to create a package.json before installing any packages.
  • Commit package.json and package-lock.json to version control, but add node_modules to .gitignore — it’s regenerated by npm install.
  • Prefer the node: prefix for built-in modules (require("node:fs")) so it’s immediately clear a module is part of Node.js itself, not a third-party package.
  • Use the REPL for quick one-off checks, but write real programs in files so they’re saved, testable, and repeatable.
  • Check node -v whenever something behaves unexpectedly — a surprising number of “bugs” are really just an outdated or unexpected Node version.

Practice Exercises

  • Install Node.js if you haven’t already, then run node -v and npm -v to confirm both are on your PATH.
  • Write a script info.js that prints your Node.js version, your OS platform, and the current working directory (hint: look up process.cwd()). Run it with node info.js.
  • Extend the HTTP server from Example 3 so that visiting /time responds with the current date and time as plain text, while every other path still returns the original greeting (hint: check req.url inside the request handler).

Summary

  • Node.js runs JavaScript outside the browser by pairing the V8 engine with libuv, which adds an event loop, a thread pool, and OS-level access to files, networking, and processes.
  • Node.js is single-threaded and asynchronous by default: slow I/O is handed off to libuv, and callbacks run on the main thread once results are ready.
  • Install an LTS version, then verify it with node -v and npm -v.
  • Run scripts with node file.js, or explore interactively with the REPL by running node alone.
  • process is a global object giving you process.argv, process.env, process.version, and more, without any require call.
  • A script exits automatically once nothing is left pending; an active server or timer keeps the event loop — and the process — alive.
  • Use npm init -y to scaffold package.json, and always run npm install before running code that depends on packages you haven’t installed yet.