# I Installed Node.js and Accidentally Became a Backend Developer

I had been writing JavaScript in the browser for a while. Console logs, DOM manipulation, making buttons change color. It felt productive until someone asked me to build something that saved data. "Just use Node," they said. I did not know what Node was. I thought JavaScript only ran inside Chrome.

Turns out JavaScript escaped the browser in 2009. A developer named Ryan Dahl took Chrome's V8 engine, the thing that actually reads and runs your JavaScript code, and wrapped it in a program that could run on your operating system directly. No browser needed. That program is Node.js. You write JavaScript in a file, you run that file with the `node` command, and your operating system executes it the same way it would run a Python script or a C program. The language is the same. The environment is completely different.

The browser gives you `document`, `window`, `alert()`. Node gives you the filesystem, network access, the ability to start an HTTP server. Same syntax, different world underneath.

I want to walk through everything it takes to go from "I have never touched Node" to "I just wrote a working web server." No frameworks, no npm packages, just Node itself and what it ships with.

* * *

## Installing Node.js

Go to [nodejs.org](https://nodejs.org). You will see two versions. One says LTS (Long Term Support), the other says Current. Pick LTS. Current has newer features but LTS is what companies actually use in production, and it gets security patches for longer. You are not going to need bleeding-edge features right now.

The installer works on Windows, macOS, and Linux. Download it, run it, click through the defaults. On Linux you can also install through your package manager, but the version in `apt` or `yum` is often outdated. The official installer or a version manager is a better bet.

If you are on macOS or Linux and want to manage multiple Node versions later, look into [nvm](https://github.com/nvm-sh/nvm). It lets you install and switch between Node versions with a single command. On Windows, [nvm-windows](https://github.com/coreybutler/nvm-windows) does the same thing. You do not need this today, but knowing it exists saves future headaches.

```bash
# Install via nvm (macOS/Linux) if you want this route
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
nvm install --lts
nvm use --lts
```

Or just use the installer from the website. Both work.

* * *

## Checking that it actually installed

Open a terminal. On Windows that is PowerShell or Command Prompt. On macOS it is Terminal. On Linux, whatever terminal emulator you have.

```bash
node -v
```

```plaintext
v22.15.0
```

If you see a version number, Node is installed. If you see "command not found," the installer either failed or your terminal does not know where Node lives (a PATH issue, which I covered in the Linux post if you want the full story on what PATH actually is).

Check npm too:

```bash
npm -v
```

```plaintext
10.9.2
```

npm is Node's package manager. It comes bundled with Node automatically. You will use it later to install libraries, but for this post we are not touching it. Everything here uses only what Node ships with out of the box.

Your version numbers will probably be different from mine. That is fine. As long as both commands return a number and not an error, you are good.

* * *

## The REPL: JavaScript without a file

Before writing any files, there is something worth trying. Type `node` in your terminal with no arguments:

```bash
node
```

```plaintext
Welcome to Node.js v22.15.0.
Type ".help" for more information.
>
```

That `>` prompt is the REPL. REPL stands for Read, Eval, Print, Loop. It reads what you type, evaluates it as JavaScript, prints the result, and loops back waiting for more input. It is an interactive JavaScript sandbox right in your terminal.

```js
> 2 + 2
4
> "hello".toUpperCase()
'HELLO'
> const x = 10
undefined
> x * 3
30
> [1, 2, 3].map(n => n * 2)
[ 2, 4, 6 ]
```

Every line executes immediately. No file, no save, no refresh. The `undefined` you see after `const x = 10` is just the REPL telling you that the assignment expression itself did not return a value. It is not an error.

The REPL is useful for quick experiments. You want to check how a string method works, or test whether your regex matches something, or try out an array operation before putting it in your code. Faster than opening a browser console, and it runs the exact same V8 engine.

A few REPL commands worth knowing:

```plaintext
.help     shows available commands
.exit     quits the REPL (Ctrl+C twice also works)
.editor   opens multi-line mode so you can paste or type a function
```

Try `.editor` mode:

```js
> .editor
// Entering editor mode (Ctrl+D to finish, Ctrl+C to cancel)
function greet(name) {
  return `hey ${name}, welcome to Node`;
}
// press Ctrl+D
> greet("Saumya")
'hey Saumya, welcome to Node'
```

The REPL is a scratchpad. Use it when you want to test one thing quickly. For anything longer than a few lines, write a file.

To exit: type `.exit` or hit Ctrl+C twice.

* * *

## Your first JavaScript file

Create a folder somewhere. Call it whatever you want. I will use `node-basics`.

```bash
mkdir node-basics
cd node-basics
```

Create a file called `hello.js`. You can use any text editor. VS Code, Sublime, Vim, Nano, Notepad, it does not matter. The file just needs to end in `.js`.

```js
// hello.js
const name = "Saumya";
const hour = new Date().getHours();

let greeting;

if (hour < 12) {
  greeting = "Good morning";
} else if (hour < 17) {
  greeting = "Good afternoon";
} else {
  greeting = "Good evening";
}

console.log(`${greeting}, ${name}.`);
console.log(`It is currently ${hour}:00 hours.`);
console.log("This is running outside the browser.");
```

Nothing fancy. Variables, a conditional, template literals, `console.log`. The same JavaScript you already know. The only difference is where it runs.

* * *

## Running it

```bash
node hello.js
```

```plaintext
Good afternoon, Saumya.
It is currently 14:00 hours.
This is running outside the browser.
```

That is it. Node read your file, executed the JavaScript, printed the output to your terminal. No browser opened. No HTML page. Your operating system ran JavaScript the same way it runs any other program.

What just happened under the hood is worth understanding, even briefly.

![](https://cdn.hashnode.com/uploads/covers/678b775e773554ab7117f20a/bdc62a81-b48a-44de-8068-8317e27f0e93.png align="center")

When you type `node hello.js`, the Node.js runtime starts up. It hands your code to V8, which compiles the JavaScript into machine code that your CPU can actually execute. V8 does not interpret JavaScript line by line like some languages do. It compiles it. That is why Node is fast enough to run web servers handling thousands of requests.

The output goes to stdout, which is what your terminal is reading. `console.log` in Node writes to stdout. In the browser, `console.log` writes to the browser's developer console. Same function name, different destination.

Try adding an error to the file on purpose:

```js
// error-test.js
console.log("this will print");
undefinedVariable.doSomething();
console.log("this will NOT print");
```

```bash
node error-test.js
```

```plaintext
this will print
/home/you/node-basics/error-test.js:2
undefinedVariable.doSomething();
^

ReferenceError: undefinedVariable is not defined
    at Object.<anonymous> (/home/you/node-basics/error-test.js:2:1)
```

Node gives you the file name, the line number, a caret pointing at the exact spot, and the error type. Read these. They tell you what went wrong and where. The first line printed because Node executes sequentially until it hits an unhandled error, then it stops. The third line never ran.

* * *

## A few things Node has that the browser does not

Before we build the server, I want to show a couple of things that make Node different from browser JavaScript. You cannot access these in Chrome's console.

```js
// node-extras.js

// __dirname: the absolute path to the folder this file is in
console.log("This file lives in:", __dirname);

// __filename: the absolute path to this file itself
console.log("This file is:", __filename);

// process: information about the running Node process
console.log("Node version:", process.version);
console.log("Operating system:", process.platform);
console.log("Current directory:", process.cwd());

// process.argv: command line arguments passed to the script
console.log("Arguments:", process.argv);
```

```bash
node node-extras.js hello world
```

```plaintext
This file lives in: /home/you/node-basics
This file is: /home/you/node-basics/node-extras.js
Node version: v22.15.0
Operating system: linux
Current directory: /home/you/node-basics
Arguments: [ '/usr/bin/node', '/home/you/node-basics/node-extras.js', 'hello', 'world' ]
```

`process.argv` is an array. The first element is always the path to the Node binary. The second is the path to your script. Everything after that is whatever you typed after the filename. This is how CLI tools read your input.

A quick practical use:

```js
// greet-cli.js
const name = process.argv[2] || "stranger";
console.log(`Hello, ${name}.`);
```

```bash
node greet-cli.js Ravi
```

```plaintext
Hello, Ravi.
```

```bash
node greet-cli.js
```

```plaintext
Hello, stranger.
```

You just built a tiny CLI tool. Nothing installed, no libraries, just `process.argv` and a fallback.

* * *

## Writing a Hello World server

This is the part where Node stops feeling like "JavaScript that runs in the terminal" and starts feeling like something genuinely different. We are going to write an HTTP server from scratch. No Express, no framework, just the `http` module that ships with Node.

```js
// server.js
const http = require("http");

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

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

```bash
node server.js
```

```plaintext
Server running at http://localhost:3000
```

Open your browser and go to `http://localhost:3000`. You will see "Hello from Node.js" on the page. Your terminal is still running. The server is sitting there, waiting for requests.

Let me break down what each part does.

`require("http")` loads Node's built-in HTTP module. This module knows how to speak HTTP, the protocol your browser uses to talk to websites. You did not install this. It comes with Node.

`http.createServer()` creates a server object. The function you pass it runs every single time someone makes a request to your server. That function gets two arguments: `req` (the incoming request) and `res` (the response you send back).

`res.writeHead(200, { "Content-Type": "text/plain" })` sets the HTTP status code to 200 (which means "OK, everything is fine") and tells the browser the response body is plain text.

`res.end("Hello from Node.js\n")` sends the actual response body and closes the connection. If you forget `res.end()`, the browser will hang forever waiting for the response to finish.

`server.listen(3000)` tells the server to start listening on port 3000. A port is just a number that identifies which program should receive incoming network traffic. Port 80 is the default for HTTP, port 443 for HTTPS. We use 3000 because it is unlikely to conflict with anything else running on your machine.

The callback in `listen()` runs once the server is ready. That is where the console.log fires.

Here is what happens when your browser visits `localhost:3000`:

![](https://cdn.hashnode.com/uploads/covers/678b775e773554ab7117f20a/a138fa25-9b75-49b8-843e-c1a7b4901bb4.png align="center")

To stop the server, go back to your terminal and press Ctrl+C. The process exits and the port is freed.

* * *

## Making the server do more than one thing

A server that returns the same text for every URL is not very useful. Let's handle different routes:

```js
// server-routes.js
const http = require("http");

const server = http.createServer((req, res) => {
  // req.url contains the path the browser requested
  // req.method contains GET, POST, etc.

  if (req.url === "/" && req.method === "GET") {
    res.writeHead(200, { "Content-Type": "text/html" });
    res.end("<h1>Home page</h1><p>Try visiting /about or /time</p>");

  } else if (req.url === "/about" && req.method === "GET") {
    res.writeHead(200, { "Content-Type": "text/html" });
    res.end("<h1>About</h1><p>This is a Node.js server with no framework.</p>");

  } else if (req.url === "/time" && req.method === "GET") {
    const now = new Date().toLocaleTimeString();
    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(JSON.stringify({ time: now }));

  } else {
    res.writeHead(404, { "Content-Type": "text/plain" });
    res.end("404 - not found\n");
  }
});

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

```bash
node server-routes.js
```

Now try these in your browser:

*   `http://localhost:3000/` returns an HTML page
    
*   `http://localhost:3000/about` returns a different HTML page
    
*   `http://localhost:3000/time` returns JSON with the current time
    
*   `http://localhost:3000/anything-else` returns a 404
    

Notice the `/time` route sets `Content-Type` to `application/json` and uses `JSON.stringify` to turn a JavaScript object into a JSON string. This is how APIs work. The browser (or any HTTP client) gets back structured data it can parse and use.

The `req.url` and `req.method` check is the most primitive form of routing. In a real app you would use Express or Fastify to handle this, because writing `if/else` blocks for every URL gets old fast. But seeing it done manually first is how you understand what those frameworks actually do underneath. They are reading `req.url` and `req.method` and matching patterns, just with nicer syntax.

* * *

## A habit worth building right now

Every time you modify your server code, you have to stop the server (Ctrl+C) and restart it (`node server.js`). This gets annoying within about ten minutes.

Node 22 has a built-in watch mode:

```bash
node --watch server.js
```

Now when you save changes to `server.js`, Node automatically restarts the server. No extra tools needed. If you are on an older Node version, `nodemon` is the classic npm package that does the same thing, but if your Node supports `--watch`, use that first.

* * *

## What is actually different from browser JavaScript

By this point you have written and run JavaScript outside the browser. The syntax is identical, but the environment is not. Here is what changed:

The browser gives you `window`, `document`, `localStorage`, `fetch` (in modern browsers), `alert()`, and the entire DOM. None of these exist in Node. If you try `document.getElementById` in a Node script, you get a ReferenceError. There is no document. There is no HTML page. There is no DOM.

Node gives you `process`, `require()` (or `import` with ES modules), `__dirname`, `__filename`, access to the filesystem through the `fs` module, the ability to create servers with `http`, read and write files, run child processes, interact with your operating system. None of these exist in the browser. If you try `require("fs")` in Chrome's console, it will not work.

![](https://cdn.hashnode.com/uploads/covers/678b775e773554ab7117f20a/0738c178-8934-47f5-aa54-3f21bd4b1f89.png align="center")

The language is one thing. The environment is another. People mix these up constantly when starting out. If a tutorial shows `document.querySelector` it is browser JavaScript. If it shows `require("http")` it is Node JavaScript. The JavaScript itself is identical. What changes is the stuff around it.

* * *

## Common mistakes when you are starting out

I am putting these here because I made all of them.

**Running** `node` **with no filename and wondering why your file did not execute.** Typing `node` alone opens the REPL. Typing `node server.js` runs the file. They do different things.

**Editing the file and expecting the running server to pick up changes.** Node reads your file once when it starts. If you change the file, the running process does not know. Restart it, or use `--watch`.

**Forgetting** `res.end()` **in your server.** The browser will just keep spinning. The connection stays open, waiting for more data that never comes. Every response needs to be closed with `res.end()`.

**Using** `require` **for a file and forgetting the path prefix.** `require("http")` loads a built-in module. `require("myfile")` tries to find a package called "myfile" in node\_modules. `require("./myfile")` loads a file relative to the current file. The `./` matters.

**Port already in use.** If you see `EADDRINUSE` when starting your server, another process is already using that port. Either that process is still running from last time (check with `lsof -i :3000` on macOS/Linux or `netstat -ano | findstr :3000` on Windows), or pick a different port number.

* * *

## Where to go from here

At this point you can install Node, run JavaScript files, use the REPL for quick tests, and write a basic HTTP server. That is enough to start building things.

The next post in this series covers npm and packages: what `package.json` is, how to install and use libraries, and the difference between dependencies and devDependencies. After that we will get into Express, which takes the manual routing we did here and makes it not terrible.

For now, the best thing you can do is experiment. Write scripts that read command-line arguments and do something with them. Add more routes to your server. Try returning different content types. Break things and read the error messages.

* * *

## References

*   [Node.js official site](https://nodejs.org) - download and installation
    
*   [Node.js docs: HTTP module](https://nodejs.org/docs/latest/api/http.html) - the module we used to build the server
    
*   [Node.js docs: Process](https://nodejs.org/docs/latest/api/process.html) - everything about the `process` object
    
*   [nvm (Node Version Manager)](https://github.com/nvm-sh/nvm) - manage multiple Node versions on macOS/Linux
    
*   [nvm-windows](https://github.com/coreybutler/nvm-windows) - same thing for Windows
    
*   [V8 JavaScript engine](https://v8.dev/) - the engine inside both Chrome and Node
    
*   [MDN: JavaScript reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript) - when you need to look up any JS syntax
