Follow AiTechWorlds on LinkedIn for professional AI content!Follow Now →
18 minLesson 30 of 35
Node.js & Tooling

Node.js: JavaScript on the Server

Node.js: JavaScript on the Server

Node.js lets you run JavaScript outside the browser — on servers, in build tools, on the command line, and in scripts. It's what powers npm, Vite, Next.js, and thousands of backend services. The JavaScript you already know transfers directly.

What Makes Node.js Different

In the browser:

  • window, document, localStorage, DOM APIs
  • Sandboxed — can't access the file system

In Node.js:

  • process, __dirname, __filename
  • Full file system access, network sockets, environment variables
  • No DOM — it's just JavaScript + Node APIs
// This is valid Node.js — no browser needed
const fs = require("fs");
const path = require("path");

const content = fs.readFileSync("data.txt", "utf8");
console.log(content);

Running Node.js

# Run a file
node app.js
node --watch app.js  # auto-restart on changes (Node 18+)

# REPL (interactive shell)
node
> 2 + 2
4
> .exit

# Check version
node --version

Core Modules

// File System
const fs = require("fs");
const { readFileSync, writeFileSync, existsSync } = require("fs");
const fsPromises = require("fs/promises");  // async versions

// Path utilities
const path = require("path");
path.join(__dirname, "data", "users.json");  // cross-platform paths
path.resolve("./config.json");  // absolute path
path.extname("file.jpg");       // ".jpg"
path.basename("/dir/file.txt"); // "file.txt"

// OS information
const os = require("os");
os.platform();    // "win32", "linux", "darwin"
os.cpus().length; // number of CPU cores
os.homedir();     // "/Users/alice"

// Child processes
const { execSync, exec, spawn } = require("child_process");
execSync("git status");  // synchronous

// HTTP server (low-level)
const http = require("http");

// Events
const { EventEmitter } = require("events");

File System Operations

const fs = require("fs/promises");
const path = require("path");

async function readConfig() {
    try {
        const content = await fs.readFile("config.json", "utf8");
        return JSON.parse(content);
    } catch (err) {
        if (err.code === "ENOENT") return {};  // file not found
        throw err;
    }
}

async function writeData(filename, data) {
    await fs.mkdir(path.dirname(filename), { recursive: true });
    await fs.writeFile(filename, JSON.stringify(data, null, 2));
}

// List directory contents
const files = await fs.readdir("./src");
const stats = await fs.stat("./package.json");
stats.isFile();      // true
stats.isDirectory(); // false
stats.size;          // bytes

// Watch for file changes
const watcher = fs.watch("./src", { recursive: true }, (eventType, filename) => {
    console.log(`${eventType}: ${filename}`);
});

Environment Variables

// Read environment variables
const PORT = process.env.PORT ?? 3000;
const DB_URL = process.env.DATABASE_URL;
const NODE_ENV = process.env.NODE_ENV ?? "development";
const IS_PROD = NODE_ENV === "production";

// Set in terminal
// PORT=8080 node app.js

// .env file (with dotenv package)
require("dotenv").config();
// or in modern Node.js (--env-file flag):
// node --env-file=.env app.js

// process.env is always a string
const port = parseInt(process.env.PORT ?? "3000", 10);

// Check for required environment variables
function requireEnv(name) {
    const value = process.env[name];
    if (!value) throw new Error(`Environment variable ${name} is required`);
    return value;
}

const apiKey = requireEnv("API_KEY");

CLI Scripts

// cli.js
const args = process.argv.slice(2);  // remove 'node' and script path

// node cli.js --name Alice --verbose
const flags = {};
for (let i = 0; i < args.length; i++) {
    if (args[i].startsWith("--")) {
        const key = args[i].slice(2);
        flags[key] = args[i + 1] && !args[i + 1].startsWith("--") ? args[++i] : true;
    }
}

console.log(flags.name);     // "Alice"
console.log(flags.verbose);  // true

// Exit codes
process.exit(0);   // success
process.exit(1);   // error

// Clean up on exit
process.on("exit", (code) => {
    console.log(`Exiting with code ${code}`);
});

process.on("SIGINT", () => {
    console.log("\nCtrl+C pressed — shutting down");
    cleanup();
    process.exit(0);
});

Simple HTTP Server

const http = require("http");

const server = http.createServer((req, res) => {
    console.log(`${req.method} ${req.url}`);
    
    if (req.url === "/health") {
        res.writeHead(200, { "Content-Type": "application/json" });
        res.end(JSON.stringify({ status: "ok" }));
        return;
    }
    
    res.writeHead(404);
    res.end("Not found");
});

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

In practice, you'd use Express, Fastify, or Next.js instead of raw http. But knowing the underlying API clarifies what frameworks do for you.

__dirname and __filename

// CommonJS (require/module.exports)
__dirname;   // absolute path of the current directory
__filename;  // absolute path of the current file

// ESM (import/export) — __dirname doesn't exist, use this instead:
import { fileURLToPath } from "url";
import { dirname } from "path";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

Next lesson: npm — managing packages, scripts, and dependencies.

📱

Get this course's notes on Telegram!

Free cheat sheets, summaries & practice exercises

Get Notes Free →
!