
Node.js lets you run JavaScript outside the browser, on your own machine or a server. This guide walks through installation, your first script, the REPL, and a working HTTP server, with no frameworks involved.
JavaScript lived in the browser for most of its life. It needed a webpage to run, a browser to execute it. Node.js changed that. It took the V8 engine that Chrome uses to run JavaScript and made it available as a standalone runtime. You can now run .js files the same way you'd run a Python or Ruby script: directly from your terminal.
The recommended way to install Node.js is through a version manager. It lets you switch between versions easily and avoids permission issues that come with installing Node.js globally via the official installer.
nvm (Node Version Manager) is the standard choice on macOS and Linux:
bash# Install nvm (check https://github.com/nvm-sh/nvm for the latest command) curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash # Install the latest LTS version of Node.js nvm install --lts # Use it nvm use --lts
On Windows, use nvm-windows, which works the same way but is a separate project built for the Windows environment.
If you prefer a direct install, download the LTS (Long Term Support) version from nodejs.org. LTS is the stable release. Avoid "Current" unless you specifically need the latest features.
Open a new terminal window (the version manager needs a fresh shell to be on your PATH):
bashnode --version # v20.x.x npm --version # 10.x.x
You get two things when you install Node.js: the node runtime itself and npm, the package manager. Both should show version numbers. If you see "command not found," restart your terminal or check that your version manager initialized correctly in your shell's config file (.bashrc, .zshrc, config.fish, etc.).
Before writing files, it helps to understand the REPL. REPL stands for Read, Evaluate, Print, Loop. It's an interactive shell that reads a line of JavaScript, evaluates it, prints the result, and loops back waiting for the next input.
┌──────────────────────────────────┐ │ Node REPL │ │ │ │ Read ← you type code │ │ Evaluate ← Node runs it │ │ Print ← result appears │ │ Loop ← waits for next input │ └──────────────────────────────────┘
Start it by typing node with no arguments:
bashnode
You'll see a > prompt. Try some JavaScript:
javascript> 2 + 2 4 > "hello".toUpperCase() 'HELLO' > const arr = [1, 2, 3] undefined > arr.map(x => x * 2) [ 2, 4, 6 ]
The REPL is useful for quickly testing a piece of logic without creating a file. Exit with .exit or Ctrl + C twice.
Make a directory for your project and create a file:
bashmkdir my-first-node-app cd my-first-node-app touch index.js
Open index.js in your editor and write:
javascript// index.js const message = "Hello from Node.js"; console.log(message); const numbers = [1, 2, 3, 4, 5]; const doubled = numbers.map((n) => n * 2); console.log("Doubled:", doubled);
bashnode index.js
Hello from Node.js Doubled: [ 2, 4, 6, 8, 10 ]
That's it. Node.js reads the file, executes it top-to-bottom, and exits. No browser, no HTML.
┌─────────────┐ ┌────────────────────┐ ┌─────────────────┐ │ index.js │────►│ node runtime (V8) │────►│ Terminal │ │ (your code)│ │ parses + executes │ │ output printed │ └─────────────┘ └────────────────────┘ └─────────────────┘
Node.js doesn't need a webpage, a browser, or a framework. It's just your file and the runtime.
Node.js ships with a built-in http module. No installation needed, no Express. This is as close to the metal as it gets:
javascript// server.js const http = require("http"); const server = http.createServer((req, res) => { res.writeHead(200, { "Content-Type": "text/plain" }); res.end("Hello, World!"); }); server.listen(3000, () => { console.log("Server running at http://localhost:3000"); });
Run it:
bashnode server.js # Server running at http://localhost:3000
Open your browser and visit http://localhost:3000. You'll see "Hello, World!" as plain text.
Here's what each piece does:
http.createServer() takes a callback that fires on every incoming request. It receives a request object (req) and a response object (res).res.writeHead(200, {...}) sets the HTTP status code and headers.res.end() sends the response body and closes the connection.server.listen(3000) tells Node to start accepting connections on port 3000.Browser Node.js Server │ │ │ GET http://localhost:3000 │ │─────────────────────────────►│ │ │ req received │ │ callback runs │ │ res.end("Hello, World!") │ 200 OK, "Hello, World!" │ │◄─────────────────────────────│
Unlike node index.js, this process stays running. It doesn't exit after the first request. It sits there, listening, handling any request that comes in. To stop it, press Ctrl + C.
require IsYou'll notice const http = require('http'). This is CommonJS module syntax, the original way Node.js loads code. require('http') loads a built-in Node.js module. require('./utils') would load a local file. When you start using npm packages, it's still require('package-name') (or import if you use ESM).
You'll see import syntax in modern Node.js projects too, but require is what you'll encounter in most beginner examples, and it's worth knowing both exist.
node --version and npm --version confirm a working installation.node in your terminal and start experimenting..js file with node filename.js. Node executes it top-to-bottom and exits.http module is enough to create a working web server. You don't need Express to get started.This is the foundation everything else builds on. Frameworks like Express.js are just abstractions over this same http module. Understanding what's underneath makes the framework click faster when you get there.
Related posts based on tags, category, and projects
Middleware is code that runs between a request arriving and a response being sent. In Express, every middleware function in the chain gets a chance to inspect, modify, or stop a request. This post covers what middleware is, how `next()` controls the flow, and where it gets used in real applications.
Node.js isn't fast because of raw processing power. It's fast because it never waits around when there's work to do. This post covers the architectural decisions that make Node.js well-suited for high-concurrency web applications.
REST is a set of conventions for designing APIs that are predictable, consistent, and easy to work with. This post covers what REST means, how it maps to HTTP, and how to build a clean resource-based API in Express.
The event loop is what lets Node.js handle thousands of concurrent operations on a single thread. This post builds that mental model from scratch, covering the call stack, task queue, and how async operations move through the system.