Node.js Core Concepts Guide
Node.js Core Concepts Guide
Overview
The event loop is the heart of Node.js, enabling non-blocking I/O operations. It allows Node.js to handle
multiple operations concurrently without creating multiple threads.
Key Concepts
Single-threaded: Main thread handles JavaScript execution
Code Examples
console.log('Start');
setTimeout(() => {
console.log('Timeout callback');
}, 0);
setImmediate(() => {
console.log('Immediate callback');
});
process.nextTick(() => {
console.log('Next tick callback');
});
console.log('End');
// Output order:
// Start
// End
// Next tick callback
// Immediate callback
// Timeout callback
const fs = require('fs');
// Non-blocking (asynchronous)
console.log('Reading file asynchronously...');
fs.readFile('example.txt', 'utf8', (err, data) => {
if (err) {
console.error('Error:', err);
return;
}
console.log('File content:', data);
});
console.log('This runs immediately');
const fs = require('fs').promises;
// Using Promises
function readFilePromise() {
return fs.readFile('example.txt', 'utf8')
.then(data => {
console.log('Promise - File content:', data);
return data;
})
.catch(err => {
console.error('Promise - Error:', err);
});
}
// Using Async/Await
async function readFileAsync() {
try {
const data = await fs.readFile('example.txt', 'utf8');
console.log('Async/Await - File content:', data);
return data;
} catch (err) {
console.error('Async/Await - Error:', err);
}
}
// Usage
readFilePromise();
readFileAsync();
Overview
Node.js uses the CommonJS module system (and now supports ES modules). NPM is the package
manager for Node.js.
Types of Modules
Core modules: Built into Node.js (fs, http, path, etc.)
Local modules: Your own modules
Third-party modules: Installed via npm
Code Examples
math.js
javascript
// CommonJS Export
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
const PI = 3.14159;
// Multiple exports
module.exports = {
add,
subtract,
PI
};
// Or single export
// module.exports = add;
app.js
javascript
// CommonJS Import
const math = require('./math');
const { add, subtract } = require('./math');
console.log(math.add(5, 3)); // 8
console.log(subtract(10, 4)); // 6
console.log(math.PI); // 3.14159
ES Modules (ES6+)
math.mjs or math.js (with "type": "module" in package.json)
javascript
// ES Module Export
export function add(a, b) {
return a + b;
}
// Default export
export default function multiply(a, b) {
return a * b;
}
app.mjs
javascript
// ES Module Import
import multiply, { add, subtract, PI } from './math.mjs';
import * as math from './math.mjs';
console.log(add(5, 3)); // 8
console.log(multiply(4, 5)); // 20
console.log(math.PI); // 3.14159
package.json
json
{
"name": "my-node-app",
"version": "1.0.0",
"description": "A sample Node.js application",
"main": "app.js",
"scripts": {
"start": "node app.js",
"dev": "nodemon app.js",
"test": "jest"
},
"dependencies": {
"express": "^4.18.2",
"lodash": "^4.17.21"
},
"devDependencies": {
"nodemon": "^2.0.22",
"jest": "^29.5.0"
}
}
javascript
Overview
The fs module provides APIs for interacting with the file system. Most methods have both synchronous
and asynchronous versions.
Code Examples
Reading Files
javascript
const fs = require('fs');
const path = require('path');
// Asynchronous read
fs.readFile('data.txt', 'utf8', (err, data) => {
if (err) {
console.error('Error reading file:', err);
return;
}
console.log('File content:', data);
});
Writing Files
javascript
const fs = require('fs');
// Asynchronous write
const content = 'Hello, World!\nThis is a new file.';
fs.writeFile('output.txt', content, 'utf8', (err) => {
if (err) {
console.error('Error writing file:', err);
return;
}
console.log('File written successfully!');
});
// Append to file
fs.appendFile('output.txt', '\nAppended text', (err) => {
if (err) {
console.error('Error appending file:', err);
return;
}
console.log('Text appended successfully!');
});
// Promise-based write
async function writeFileAsync() {
try {
await fsPromises.writeFile('async-output.txt', 'Async content');
console.log('Async file written!');
} catch (err) {
console.error('Async write error:', err);
}
}
const fs = require('fs');
const path = require('path');
// Create directory
fs.mkdir('new-folder', { recursive: true }, (err) => {
if (err) {
console.error('Error creating directory:', err);
return;
}
console.log('Directory created!');
});
javascript
const fs = require('fs');
// Read stream
const readStream = fs.createReadStream('large-file.txt', 'utf8');
readStream.on('data', (chunk) => {
console.log('Received chunk:', chunk.length, 'characters');
});
readStream.on('end', () => {
console.log('Finished reading file');
});
readStream.on('error', (err) => {
console.error('Read stream error:', err);
});
// Write stream
const writeStream = fs.createWriteStream('output-stream.txt');
writeStream.write('First chunk of data\n');
writeStream.write('Second chunk of data\n');
writeStream.end('Final chunk of data\n');
writeStream.on('finish', () => {
console.log('Write stream finished');
});
Overview
The http module allows you to create HTTP servers and clients. It's the foundation for web applications
in Node.js.
Code Examples
// Parse URL
const parsedUrl = url.parse(req.url, true);
const path = parsedUrl.pathname;
const query = parsedUrl.query;
// Simple routing
if (path === '/') {
res.end('<h1>Home Page</h1>');
} else if (path === '/about') {
res.end('<h1>About Page</h1>');
} else if (path === '/api/data') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ message: 'Hello API', query }));
} else {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('<h1>404 - Page Not Found</h1>');
}
});
req.on('end', () => {
try {
const data = JSON.parse(body);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
message: 'POST request received',
data
}));
} catch (err) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Invalid JSON' }));
}
});
}
else {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Route not found' }));
}
});
server.listen(3000, () => {
console.log('Server running on port 3000');
});
res.on('end', () => {
console.log('Response:', data);
});
});
const options = {
hostname: 'localhost',
port: 3000,
path: '/data',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(postData)
}
};
res.on('end', () => {
console.log('POST Response:', data);
});
});
req.write(postData);
req.end();
}
// Usage
makeGetRequest('http://localhost:3000/');
makePostRequest();
Overview
Environment variables are used to store configuration values outside your code, making applications
more secure and flexible across different environments.
Code Examples
console.log('Environment:', nodeEnv);
console.log('Port:', port);
console.log('Database URL:', dbUrl);
if (missingVars.length > 0) {
console.error('Missing required environment variables:', missingVars);
process.exit(1);
}
bash
.env file
PORT=3000
DATABASE_URL=mongodb://localhost:27017/myapp
API_KEY=your-secret-api-key
JWT_SECRET=your-jwt-secret
NODE_ENV=development
DEBUG=true
config.js
javascript
const config = {
port: process.env.PORT || 3000,
database: {
url: process.env.DATABASE_URL || 'mongodb://localhost:27017/myapp',
options: {
useNewUrlParser: true,
useUnifiedTopology: true
}
},
jwt: {
secret: process.env.JWT_SECRET || 'default-secret',
expiresIn: process.env.JWT_EXPIRES_IN || '1h'
},
api: {
key: process.env.API_KEY
},
nodeEnv: process.env.NODE_ENV || 'development',
debug: process.env.DEBUG === 'true'
};
module.exports = config;
app.js
javascript
// Use configuration
const server = require('http').createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
environment: config.nodeEnv,
port: config.port,
timestamp: new Date().toISOString()
}));
});
server.listen(config.port, () => {
console.log(`Server running on port ${config.port}`);
});
// config/index.js
const development = {
port: 3000,
database: {
host: 'localhost',
name: 'myapp_dev'
},
logging: true,
cache: false
};
const production = {
port: process.env.PORT || 8080,
database: {
host: process.env.DB_HOST,
name: process.env.DB_NAME
},
logging: false,
cache: true
};
const test = {
port: 3001,
database: {
host: 'localhost',
name: 'myapp_test'
},
logging: false,
cache: false
};
module.exports = configs[nodeEnv];
Overview
Debugging is crucial for finding and fixing issues in your Node.js applications. There are several tools and
techniques available.
Code Examples
javascript
// Timing operations
console.time('operation');
// Some operation
setTimeout(() => {
console.timeEnd('operation');
}, 1000);
// Stack trace
function a() { b(); }
function b() { c(); }
function c() { console.trace('Trace from c()'); }
a();
bash
// Usage
dbDebug('Connecting to database...');
serverDebug('Server starting on port %d', 3000);
authDebug('User %s attempting login', '[email protected]');
// Conditional debugging
if (debug.enabled('app:database')) {
// Expensive debugging operation
dbDebug('Query results: %O', { complexObject: true });
}
bash
// app.js
function calculateSum(numbers) {
let sum = 0;
debugger; // Breakpoint
return sum;
}
bash
# Debugger commands:
# n - next line
# s - step into
# c - continue
# repl - enter REPL mode
# .exit - exit debugger
launch.json
json
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceFolder}/app.js",
"console": "integratedTerminal",
"env": {
"NODE_ENV": "development",
"DEBUG": "app:*"
}
},
{
"type": "node",
"request": "attach",
"name": "Attach to Process",
"port": 9229
}
]
}
function validateUser(user) {
if (!user.email) {
throw new ValidationError('Email is required', 'email');
}
}
// Usage
try {
validateUser({});
} catch (error) {
if (error instanceof ValidationError) {
console.error('Validation failed:', error.message, 'Field:', error.field);
} else {
console.error('Unexpected error:', error);
}
}
Performance Debugging
javascript
// Measure performance
function measurePerformance(fn, label) {
const start = performance.now();
const result = fn();
const end = performance.now();
console.log(`${label} took ${end - start} milliseconds`);
return result;
}
// Memory usage
function logMemoryUsage() {
const used = process.memoryUsage();
console.log('Memory Usage:');
for (let key in used) {
console.log(`${key}: ${Math.round(used[key] / 1024 / 1024 * 100) / 100} MB`);
}
}
// Usage
measurePerformance(() => {
// Some operation
let sum = 0;
for (let i = 0; i < 1000000; i++) {
sum += i;
}
return sum;
}, 'Sum calculation');
logMemoryUsage();
Summary
This guide covers the essential Node.js core concepts:
Each concept builds upon the others, forming the foundation for building robust Node.js applications.
Practice these examples and experiment with the code to deepen your understanding.
Next Steps
Practice building small projects using these concepts
Explore popular frameworks like Express.js