Node.js: Runtime and Fundamentals
Node.js is a JavaScript runtime for building server-side applications. This article covers Node.js fundamentals.
Introduction
Node.js provides:
- Server-side JavaScript execution
- Non-blocking I/O
- Event-driven architecture
- Module system
- Rich ecosystem
Understanding Node.js helps you:
- Build server applications
- Handle asynchronous operations
- Work with file systems
- Create APIs
- Scale applications
Node.js Installation and Setup
Installation
# โ
Good: Install Node.js
# Download from https://nodejs.org/
# โ
Good: Verify installation
node --version
npm --version
# โ
Good: Create new project
mkdir my-app
cd my-app
npm init -y
# โ
Good: Install dependencies
npm install express
npm install --save-dev nodemon
Package.json
{
"name": "my-app",
"version": "1.0.0",
"description": "My Node.js application",
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js",
"test": "jest"
},
"keywords": ["node", "javascript"],
"author": "Your Name",
"license": "MIT",
"dependencies": {
"express": "^4.18.0"
},
"devDependencies": {
"nodemon": "^2.0.0",
"jest": "^29.0.0"
}
}
Node.js Modules
CommonJS Modules
// math.js
// โ
Good: Export functions
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
module.exports = {
add,
subtract
};
// app.js
// โ
Good: Import modules
const math = require('./math');
console.log(math.add(5, 3)); // 8
console.log(math.subtract(5, 3)); // 2
// โ
Good: Destructuring imports
const { add, subtract } = require('./math');
console.log(add(5, 3)); // 8
console.log(subtract(5, 3)); // 2
// โ
Good: Built-in modules
const fs = require('fs');
const path = require('path');
const http = require('http');
const filePath = path.join(__dirname, 'file.txt');
const content = fs.readFileSync(filePath, 'utf-8');
console.log(content);
ES Modules
// math.js
// โ
Good: Export with ES modules
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
// app.js
// โ
Good: Import ES modules
import { add, subtract } from './math.js';
console.log(add(5, 3)); // 8
console.log(subtract(5, 3)); // 2
// โ
Good: Default export
// utils.js
export default function greet(name) {
return `Hello, ${name}!`;
}
// app.js
import greet from './utils.js';
console.log(greet('John')); // Hello, John!
File System Operations
Reading and Writing Files
const fs = require('fs');
const path = require('path');
// โ
Good: Read file synchronously
const content = fs.readFileSync('file.txt', 'utf-8');
console.log(content);
// โ
Good: Read file asynchronously
fs.readFile('file.txt', 'utf-8', (err, data) => {
if (err) {
console.error('Error reading file:', err);
return;
}
console.log(data);
});
// โ
Good: Read file with promises
fs.promises.readFile('file.txt', 'utf-8')
.then(data => console.log(data))
.catch(err => console.error('Error:', err));
// โ
Good: Write file
fs.writeFileSync('output.txt', 'Hello, World!');
// โ
Good: Write file asynchronously
fs.writeFile('output.txt', 'Hello, World!', (err) => {
if (err) {
console.error('Error writing file:', err);
return;
}
console.log('File written successfully');
});
// โ
Good: Append to file
fs.appendFileSync('log.txt', 'New log entry\n');
// โ
Good: Delete file
fs.unlinkSync('file.txt');
// โ
Good: List directory contents
const files = fs.readdirSync('./');
console.log(files);
// โ
Good: Check if file exists
if (fs.existsSync('file.txt')) {
console.log('File exists');
}
Event Loop and Asynchronous Operations
Understanding the Event Loop
// โ
Good: Event loop demonstration
console.log('Start');
setTimeout(() => {
console.log('Timeout 1');
}, 0);
Promise.resolve()
.then(() => {
console.log('Promise 1');
});
console.log('End');
// Output:
// Start
// End
// Promise 1
// Timeout 1
// โ
Good: Microtasks vs Macrotasks
console.log('Script start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
Promise.resolve()
.then(() => {
console.log('Promise 1');
})
.then(() => {
console.log('Promise 2');
});
console.log('Script end');
// Output:
// Script start
// Script end
// Promise 1
// Promise 2
// setTimeout
Callbacks and Promises
// โ
Good: Callback pattern
function readUserData(userId, callback) {
setTimeout(() => {
const user = { id: userId, name: 'John' };
callback(null, user);
}, 1000);
}
readUserData(1, (err, user) => {
if (err) {
console.error('Error:', err);
return;
}
console.log('User:', user);
});
// โ
Good: Promise pattern
function readUserDataPromise(userId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const user = { id: userId, name: 'John' };
resolve(user);
}, 1000);
});
}
readUserDataPromise(1)
.then(user => console.log('User:', user))
.catch(err => console.error('Error:', err));
// โ
Good: Async/await pattern
async function getUserData(userId) {
try {
const user = await readUserDataPromise(userId);
console.log('User:', user);
} catch (err) {
console.error('Error:', err);
}
}
getUserData(1);
Streams
Working with Streams
const fs = require('fs');
// โ
Good: Read stream
const readStream = fs.createReadStream('large-file.txt', {
encoding: 'utf-8',
highWaterMark: 16 * 1024 // 16KB chunks
});
readStream.on('data', (chunk) => {
console.log('Received chunk:', chunk.length);
});
readStream.on('end', () => {
console.log('Stream ended');
});
readStream.on('error', (err) => {
console.error('Stream error:', err);
});
// โ
Good: Write stream
const writeStream = fs.createWriteStream('output.txt');
writeStream.write('Hello, ');
writeStream.write('World!');
writeStream.end();
writeStream.on('finish', () => {
console.log('Write stream finished');
});
// โ
Good: Pipe streams
fs.createReadStream('input.txt')
.pipe(fs.createWriteStream('output.txt'));
// โ
Good: Transform stream
const { Transform } = require('stream');
const upperCaseTransform = new Transform({
transform(chunk, encoding, callback) {
this.push(chunk.toString().toUpperCase());
callback();
}
});
fs.createReadStream('input.txt')
.pipe(upperCaseTransform)
.pipe(fs.createWriteStream('output.txt'));
HTTP Server
Creating a Basic Server
const http = require('http');
// โ
Good: Create HTTP server
const server = http.createServer((req, res) => {
if (req.url === '/' && req.method === 'GET') {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('<h1>Hello, World!</h1>');
} else if (req.url === '/api/users' && req.method === 'GET') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify([
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' }
]));
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not Found');
}
});
server.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
// โ
Good: Handle POST requests
const server = http.createServer((req, res) => {
if (req.url === '/api/users' && req.method === 'POST') {
let body = '';
req.on('data', (chunk) => {
body += chunk.toString();
});
req.on('end', () => {
const user = JSON.parse(body);
console.log('Received user:', user);
res.writeHead(201, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ id: 1, ...user }));
});
}
});
server.listen(3000);
Environment Variables
Working with Environment Variables
// โ
Good: Access environment variables
console.log(process.env.NODE_ENV);
console.log(process.env.PORT);
// โ
Good: Use dotenv package
// .env
NODE_ENV=development
PORT=3000
DATABASE_URL=mongodb://localhost:27017/mydb
// app.js
require('dotenv').config();
const port = process.env.PORT || 3000;
const dbUrl = process.env.DATABASE_URL;
console.log(`Server running on port ${port}`);
console.log(`Database URL: ${dbUrl}`);
// โ
Good: Set default values
const nodeEnv = process.env.NODE_ENV || 'development';
const port = process.env.PORT || 3000;
const debug = process.env.DEBUG === 'true';
console.log(`Environment: ${nodeEnv}`);
console.log(`Port: ${port}`);
console.log(`Debug: ${debug}`);
Best Practices
-
Use async/await:
// โ Good: Async/await async function getData() { try { const data = await fetchData(); return data; } catch (err) { console.error('Error:', err); } } // โ Bad: Callback hell function getData(callback) { fetchData((err, data) => { if (err) callback(err); else callback(null, data); }); } -
Handle errors properly:
// โ Good: Error handling try { const data = fs.readFileSync('file.txt', 'utf-8'); } catch (err) { console.error('Error reading file:', err); } // โ Bad: No error handling const data = fs.readFileSync('file.txt', 'utf-8'); -
Use environment variables:
// โ Good: Environment variables const port = process.env.PORT || 3000; // โ Bad: Hardcoded values const port = 3000;
Summary
Node.js is powerful. Key takeaways:
- Understand the event loop
- Use async/await for asynchronous operations
- Work with file systems
- Create HTTP servers
- Use streams for large data
- Handle errors properly
- Use environment variables
- Build scalable applications
Related Resources
- Node.js Documentation
- Node.js API Reference
- Event Loop Explained
- Streams Documentation
- File System API
Next Steps
- Learn about Express.js
- Explore Routing and Middleware
- Study Database Integration
- Practice Node.js
- Build Node.js applications
Comments