Advertisement
⚑ Frameworks

Node.js – Running JavaScript on the Server

Node.js is a JavaScript runtime built on Chrome's V8 engine that lets you run JavaScript outside the browser β€” on servers, CLIs, scripts, and desktop apps. Its event-driven, non-blocking I/O model makes it exceptionally efficient for I/O-heavy workloads like APIs, real-time apps, and microservices.

⏱️ 23 min read 🎯 Advanced πŸ“… Updated 2026

What Is Node.js?

Ryan Dahl created Node.js in 2009, embedding the V8 JavaScript engine in a C++ program that adds APIs for file system access, networking, processes, and more. Key characteristics:

  • Single-threaded event loop β€” one thread handles all I/O via callbacks/Promises, avoiding thread management complexity.
  • Non-blocking I/O β€” operations like reading files or querying databases don't block the thread; the loop continues processing while waiting for I/O.
  • Same language everywhere β€” JavaScript on client and server reduces context switching and enables code sharing.
  • npm ecosystem β€” over 2 million packages available.

Installing Node.js and Running Code

JavaScript
// Download from nodejs.org or use a version manager:
// nvm install 20   (Node Version Manager β€” recommended)
// nvm use 20

// Check versions
// $ node --version   β†’ v20.x.x
// $ npm --version    β†’ 10.x.x

// REPL (Read-Eval-Print Loop) β€” interactive shell
// $ node
// > 2 + 2
// 4
// > 'hello'.toUpperCase()
// 'HELLO'
// > .exit

// Run a file
// $ node app.js

// Basic Node.js script β€” hello.js
console.log('Hello from Node.js!');
console.log('Node version:', process.version);
console.log('Platform:', process.platform);   // 'linux', 'darwin', 'win32'
console.log('Working dir:', process.cwd());

// process.argv β€” command-line arguments
// $ node greet.js Alice
const name = process.argv[2] || 'World';
console.log(`Hello, ${name}!`);
β–Ά Output
Hello from Node.js!
Node version: v20.11.0
Platform: linux
Working dir: /home/user/my-project

Node.js Global Objects

JavaScript
// __dirname β€” absolute path of current file's directory
console.log(__dirname);  // /home/user/project/src

// __filename β€” absolute path of current file
console.log(__filename); // /home/user/project/src/app.js

// process β€” information about the running Node process
process.env.NODE_ENV = 'development';     // set env variable
console.log(process.env.PORT || 3000);    // read env variable
process.exit(0);                          // exit with code 0 (success)

// Buffer β€” handle binary data
const buf = Buffer.from('Hello');
console.log(buf);           // <Buffer 48 65 6c 6c 6f>
console.log(buf.toString()); // "Hello"

// global β€” equivalent of window in browsers (rarely used directly)
global.myConfig = { debug: true };

// setTimeout, setInterval, clearTimeout, clearInterval β€” same as browser
const timer = setTimeout(() => console.log('fired!'), 1000);
clearTimeout(timer);

Core Built-in Modules

JavaScript
// No npm install needed β€” built into Node.js

// fs β€” File System
const fs = require('fs');
// Or with ES modules: import fs from 'fs';

// Read file (async with callback)
fs.readFile('data.txt', 'utf8', (err, content) => {
  if (err) throw err;
  console.log(content);
});

// Read file (Promise-based β€” preferred)
const fsPromises = require('fs/promises');
const content = await fsPromises.readFile('data.txt', 'utf8');
console.log(content);

// Write file
await fsPromises.writeFile('output.txt', 'Hello Node!');

// path β€” cross-platform file path utilities
const path = require('path');
const filePath = path.join(__dirname, 'data', 'users.json');
console.log(path.basename(filePath)); // "users.json"
console.log(path.extname(filePath));  // ".json"
console.log(path.dirname(filePath));  // "/home/.../data"

// os β€” operating system info
const os = require('os');
console.log(os.platform());  // "linux"
console.log(os.cpus().length); // number of CPU cores
console.log(os.freemem());   // free memory in bytes
console.log(os.homedir());   // "/home/user"

npm Basics

JavaScript
// Initialize a project β€” creates package.json
// $ npm init -y

// Install a package (saved to dependencies)
// $ npm install express

// Install a dev dependency (only for development/build)
// $ npm install --save-dev nodemon

// Install globally (CLI tools)
// $ npm install -g typescript

// Remove a package
// $ npm uninstall lodash

// package.json structure
// {
//   "name": "my-app",
//   "version": "1.0.0",
//   "main": "index.js",
//   "scripts": {
//     "start": "node index.js",
//     "dev": "nodemon index.js",
//     "test": "jest"
//   },
//   "dependencies": { "express": "^4.18.0" },
//   "devDependencies": { "nodemon": "^3.0.0" }
// }

// Run scripts
// $ npm run dev
// $ npm start      (shortcut β€” no "run" needed)
// $ npm test       (shortcut β€” no "run" needed)

// npx β€” run a package without installing it globally
// $ npx create-react-app my-app
// $ npx nodemon index.js
ℹ️
Commit package.json but not node_modules

Always add node_modules/ to your .gitignore. The package.json and package-lock.json files are sufficient for any developer to recreate the exact dependency tree by running npm install.

Built-in HTTP Server

JavaScript
const http = require('http');
const fs   = require('fs/promises');
const path = require('path');

const server = http.createServer(async (req, res) => {
  const url = req.url;
  const method = req.method;

  console.log(`${method} ${url}`);

  if (url === '/' && method === 'GET') {
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.end('<h1>Hello from Node.js!</h1>');

  } else if (url === '/api/users' && method === 'GET') {
    const users = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify(users));

  } else {
    res.writeHead(404, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ error: 'Not Found' }));
  }
});

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

Express.js – The Standard Node Web Framework

Express adds routing, middleware, and a clean request/response API on top of Node's built-in http:

JavaScript
// npm install express
const express = require('express');
const app = express();

// Built-in middleware β€” parse JSON request bodies
app.use(express.json());
// Serve static files from /public folder
app.use(express.static('public'));

// Routes
app.get('/', (req, res) => {
  res.send('<h1>Welcome to Express!</h1>');
});

app.get('/api/users', async (req, res) => {
  try {
    const users = await getUsersFromDB();
    res.json(users);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// Route parameters
app.get('/api/users/:id', (req, res) => {
  const { id } = req.params;
  res.json({ id, name: 'Alice' });
});

// POST with body
app.post('/api/users', (req, res) => {
  const { name, email } = req.body;
  if (!name || !email) {
    return res.status(400).json({ error: 'name and email required' });
  }
  const newUser = { id: Date.now(), name, email };
  res.status(201).json(newUser);
});

// Custom middleware β€” runs before route handlers
app.use((req, res, next) => {
  console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
  next(); // pass to next middleware or route
});

app.listen(3000, () => console.log('Express server on port 3000'));

Browser JS vs Node.js

FeatureBrowser JavaScriptNode.js
Runtime environmentBrowser tabServer / CLI / scripts
Global objectwindowglobal
DOM APIYes (document, window)No (no browser)
File systemNo (sandboxed)Yes (fs module)
ModulesES Modules nativelyCommonJS (require) or ESM
Package managerCDN / importmapnpm / yarn / pnpm
HTTPFetch API (client)Built-in http / Express (server)
Use casesUI, interactivityAPIs, CLIs, build tools, scripts
⚠️
Node.js is not great for CPU-intensive work

Node's single-threaded model excels at I/O-bound tasks (API calls, DB queries, file reads) but struggles with CPU-heavy work (image processing, video encoding, complex math). For CPU tasks, use Worker Threads, spawn a child process, or delegate to a language better suited to computation (Python, Rust, Go).

Node.js: JavaScript on the Server

Node.js lets JavaScript run outside the browser β€” on servers, build tools, CLIs. It pairs the V8 engine with a non-blocking, event-loop architecture that makes it excel at I/O-heavy workloads (APIs, real-time apps) with a single thread.

// a minimal HTTP server β€” no framework needed
import { createServer } from "node:http";
createServer((req, res) => {
  res.writeHead(200, { "Content-Type": "application/json" });
  res.end(JSON.stringify({ ok: true }));
}).listen(3000);
FeatureMeaning
Event loopone thread, non-blocking I/O β€” many connections at once
npmthe world's largest package registry
Same languageJS on front and back end

Why Node is fast for the web: instead of one thread per request (which idles while waiting on the database), Node uses a single event loop that starts an I/O operation and moves on, handling the result via a callback/promise when it's ready. This handles thousands of concurrent connections efficiently β€” ideal for APIs and real-time (chat, streaming) apps. The trade-off: that single thread means CPU-bound work (heavy computation) blocks everything β€” offload it to worker threads or a different service. The ecosystem is the draw: npm gives access to a huge library of packages, and using JavaScript on both client and server lets teams share code and skills. Frameworks like Express and Fastify sit on top for real apps; Node also underpins nearly every front-end build tool (bundlers, linters, test runners).

πŸ‹οΈ Practical Exercise

Build a simple REST API with Express:

  1. GET /api/notes β€” return all notes (store in an in-memory array).
  2. GET /api/notes/:id β€” return a single note by ID (404 if not found).
  3. POST /api/notes β€” create a note from { title, content } body; assign an auto-increment ID.
  4. DELETE /api/notes/:id β€” remove a note; 204 on success, 404 if missing.
  5. Test with curl or a browser extension like Thunder Client.

πŸ”₯ Challenge Exercise

Build a real-time chat server using Node.js built-in http and the ws npm package for WebSockets. The server: broadcasts messages from any client to all connected clients; prepends a random username to each message; stores the last 10 messages in memory and sends them to new connections. The client: a plain HTML page with a textarea and send button that connects via new WebSocket('ws://localhost:3000'). Open two browser tabs to test real-time updates.

Summary

πŸ“‹ Summary

  • Node.js runs JavaScript on the server using Chrome's V8 engine.
  • Event-driven, non-blocking I/O makes Node efficient for API servers and real-time apps.
  • Global objects: process, __dirname, __filename, Buffer.
  • Core modules: fs (file I/O), path (path utilities), http (server), os (system info).
  • npm manages packages; package.json declares dependencies and scripts.
  • Express.js adds routing, middleware, and a clean API on top of Node's http.
  • Use npx to run CLI tools without global installation.

Interview Questions

  • What is the event loop in Node.js and how does non-blocking I/O work?
  • What is the difference between dependencies and devDependencies in package.json?
  • What is middleware in Express? Give an example of a custom middleware.
  • What is the difference between npm install and npx?
  • When is Node.js a poor choice for a task?

FAQ

What is the difference between CommonJS and ES Modules in Node.js? +

CommonJS (require() / module.exports) is Node's original module system β€” synchronous, loaded at runtime. ES Modules (import / export) are the browser-standard, asynchronous module system added to Node in v12+. To use ESM in Node, add "type": "module" to package.json or use .mjs file extensions. Modern projects prefer ESM for consistency with browser code.

How does Node.js handle multiple requests on a single thread? +

Node uses an event loop backed by libuv (a C library). When an I/O operation (database query, file read, HTTP request) is initiated, Node delegates it to the OS/thread pool and continues processing other events. When the I/O completes, a callback is placed in the event queue and the event loop picks it up on the next tick. This means Node can handle thousands of concurrent connections without spawning thousands of threads.

Should I use Express or a newer framework like Fastify or Hono? +

Express has the largest ecosystem and most tutorials β€” ideal for learning and legacy projects. Fastify is 2–4x faster and has schema-based validation built in. Hono is ultra-lightweight and edge-runtime compatible (Cloudflare Workers, Deno Deploy). For a new production API in 2026, Fastify or Hono are excellent choices; Express remains perfectly valid for most use cases.

Advertisement