Node.js
Node.js is a free, open-source, cross-platform JavaScript runtime environment that allows developers to write server-side applications using JavaScript. Historically, JavaScript was confined to running inside web browsers. Node.js changed this by wrapping Google Chrome's V8 JavaScript engine with a low-level C++ platform, enabling JavaScript execution on local machines and servers.
This guide covers the fundamental concepts, core modules, package management, and basic application development with Node.js.
1. Core Architecture and Concepts
To write efficient Node.js applications, it is helpful to understand how it handles operations under the hood.
The V8 Engine
Node.js compiles JavaScript directly into native machine code using the V8 engine (written in C++). This avoids the need for real-time interpretation and helps JavaScript execute quickly.
Non-blocking I/O and the Event Loop
Standard web servers (like traditional Apache configurations) often handle incoming requests by spawning a new thread for each connection. If a thread needs to read a file or query a database (I/O operations), it blocks further execution until the data is ready.
Node.js uses a single-threaded, event-driven, non-blocking I/O model:
- Single-threaded: Node.js executes JavaScript code on a single main thread.
- Non-blocking: When an I/O operation is initiated (such as reading a file), Node.js delegates the task to the operating system or system kernel. While the OS performs this task in the background, the main thread continues executing other code.
- The Event Loop: This mechanism monitors the execution stack and the callback queue. Once the main call stack is empty, the Event Loop takes completed asynchronous tasks (callbacks) from the queue and pushes them onto the stack to be executed.
2. Installation and Environment Setup
Installing Node.js
It is generally recommended to install Node.js using NVM (Node Version Manager). This tool allows you to install, update, and switch between multiple versions of Node.js on a single machine.
On macOS/Linux (via Terminal):
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
After installation, restart your terminal and install the Long-Term Support (LTS) version:
nvm install --lts
nvm use --lts
On Windows:
You can download the installer directly from the official Node.js website, or use the nvm-windows utility.
Verifying the Installation
Open your terminal or command prompt and run:
node -v
npm -v
These commands should output the installed versions of Node.js and NPM (Node Package Manager).
3. Module Systems: CommonJS vs. ES Modules
Node.js supports two module formats for organizing and importing code: CommonJS (CJS) and ECMAScript Modules (ESM).
CommonJS (Traditional Default)
CommonJS uses require() to import modules and module.exports to export them.
// math.js
const add = (a, b) => a + b;
module.exports = { add };
// index.js
const { add } = require('./math');
console.log(add(2, 3)); // Outputs: 5
ES Modules (Modern Standard)
To use ES Modules, add "type": "module" to your package.json file. ESM uses import and export statements.
// math.js
export const add = (a, b) => a + b;
// index.js
import { add } from './math.js';
console.log(add(2, 3)); // Outputs: 5
4. Built-in Core Modules
Node.js comes with standard utility modules. Below are three of the most frequently used.
The File System (fs) Module
The fs module allows you to interact with the file system on your computer. It provides both synchronous (blocking) and asynchronous (non-blocking) methods. Modern Node.js development typically uses the promise-based API.
import fs from 'fs/promises';
async function handleFile() {
try {
// Writing to a file
await fs.writeFile('example.txt', 'Hello, Node.js!');
// Reading from a file
const content = await fs.readFile('example.txt', 'utf-8');
console.log(content); // Outputs: Hello, Node.js!
} catch (error) {
console.error('File operation failed:', error);
}
}
handleFile();
The Path (path) Module
The path module provides utilities for working with file and directory paths across different operating systems (Windows uses backslashes \, while POSIX systems use forward slashes /).
import path from 'path';
const directory = 'users/documents';
const file = 'report.pdf';
// Safely joins paths regardless of operating system
const fullPath = path.join(directory, file);
console.log(fullPath); // Outputs: users/documents/report.pdf (or users\documents\report.pdf on Windows)
// Get extension name
console.log(path.extname(fullPath)); // Outputs: .pdf
The HTTP (http) Module
The http module allows Node.js to transfer data over the Hyper Text Transfer Protocol. It can be used to create a basic web server.
import http from 'http';
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Welcome to the native Node.js HTTP server.');
});
const PORT = 3000;
server.listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}/`);
});
5. NPM (Node Package Manager)
NPM is the package manager for Node.js and is installed automatically along with the runtime. It manages third-party libraries and modules.
Initializing a Project
To start a new project, navigate to your empty project directory in your terminal and run:
npm init -y
This generates a package.json file, which tracks project metadata, dependencies, and configuration.
Understanding package.json
A basic package.json looks like this:
{
"name": "my-node-app",
"version": "1.0.0",
"type": "module",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"express": "^4.18.2"
}
}
Installing and Uninstalling Packages
To install a package and add it to your project's dependencies:
npm install express
This downloads the package files into a directory called node_modules and records the dependency inside package.json and package-lock.json.
To install a package only for development (e.g., test runners, linters):
npm install jest --save-dev
To remove a package:
npm uninstall express
6. Building a Web Application with Express.js
While the built-in http module is useful for basic tasks, real-world web applications often use Express.js, a minimalist framework that simplifies routing, middleware integration, and request/response handling.
Setting Up Express
1. Install Express:
npm install express
2. Create an app.js file:
import express from 'express';
const app = express();
const PORT = 3000;
// Middleware to parse incoming JSON payloads
app.use(express.json());
// 1. GET Request: Read data
app.get('/', (req, res) => {
res.send('Welcome to the home page.');
});
// 2. GET Request with parameters
app.get('/users/:id', (req, res) => {
const userId = req.params.id;
res.json({ message: `Fetching user data for user ID: ${userId}` });
});
// 3. POST Request: Create data
app.post('/users', (req, res) => {
const { username, email } = req.body;
if (!username || !email) {
return res.status(400).json({ error: 'Username and email are required.' });
}
res.status(201).json({
message: 'User successfully created.',
user: { username, email }
});
});
// Start the server
app.listen(PORT, () => {
console.log(`Express server is listening at http://localhost:${PORT}`);
});
To run this application, type the following command in your terminal:
node app.js
7. Error Handling and Best Practices
Writing maintainable Node.js applications requires structured error handling and code organization.
Proper Error Handling
Always catch exceptions in asynchronous code to prevent the application process from crashing unexpectedly.
// Using try/catch blocks with async/await
app.get('/data', async (req, res, next) => {
try {
const data = await fetchDataFromDatabase();
res.json(data);
} catch (error) {
// Pass the error to the Express default error handler
next(error);
}
});
// Express Centralized Error Handler (placed after all routes)
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({
error: 'An internal server error occurred.'
});
});
Environment Variables
Avoid hardcoding sensitive information like API keys, database URLs, and port numbers. Use environment variables.
1. Install the dotenv package:
npm install dotenv
2. Create a .env file in the root directory:
PORT=8080
DATABASE_URL=mongodb://localhost:27017/mydb
3. Load the configuration in your application code:
import 'dotenv/config';
const port = process.env.PORT || 3000;
console.log(`Using port: ${port}`);
Recommended Project Directory Layout
For production-ready applications, consider organizing your files by responsibility:
my-node-app/
├── node_modules/
├── src/
│ ├── controllers/ # Request handlers (logic)
│ ├── models/ # Database schemas
│ ├── routes/ # Router files mapping paths to controllers
│ ├── middleware/ # Custom Express middlewares
│ └── app.js # Entry point of the application
├── .env
├── .gitignore
├── package.json
└── README.md
The guide was created in June 2026.