Get comfortable with Node.js core modules and essential patterns used in real-world apps.
Working with HTTP in Node.js is essential for creating web servers and handling client requests. In this chapter, we'll explore how to create HTTP servers, handle routes manually, and understand key concepts like headers, status codes, and streaming responses.
To create an HTTP server in Node.js, we'll use the built-in http module. Here's how to set up a basic server:
const http = require('http');
const server = http.createServer((req, res) => {
// Handle requests here
res.end('Hello, World!');
});
server.listen(3000, () => {
console.log('Server is running on port 3000');
});
Routes determine how your server responds to different URLs and HTTP methods. Here's an example of handling different routes:
const http = require('http');
const server = http.createServer((req, res) => {
switch (req.url) {
case '/':
res.end('Welcome to the homepage!');
break;
case '/about':
res.end('About us page');
break;
default:
res.writeHead(404);
res.end('Page not found');
}
});
Headers provide additional information about the request or response. Common status codes include:
res.writeHead(200, {
'Content-Type': 'text/plain',
'X-Custom-Header': 'Example'
});
res.end('Response with headers');
Streaming allows you to send data in chunks, which is useful for large files or real-time applications. Here's an example:
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
const chunks = ['Hello', ' ', 'World!'];
chunks.forEach(chunk => {
res.write(chunk);
});
res.end();
});
Welcome to Node.js core modules! In this chapter, we'll explore three essential modules: fs (file system), path (path handling), and os (operating system). These modules provide powerful tools for file manipulation, path resolution, and platform-specific operations.
The fs module allows you to interact with the file system. Here are some common operations:
fs.promises.readFile(path, options)
fs.writeFileSync(path, data, options)
fs.existsSync(path)
fs.mkdirSync(path, { recursive: true })
const fs = require('fs');
// Read a file
try {
const data = await fs.promises.readFile('./example.txt', 'utf8');
console.log(data);
} catch (err) {
console.error('Error reading file:', err);
}
The path module helps work with file paths in a platform-independent way. Key methods include:
path.join(...paths)
path.dirname(path)
path.extname(path)
path.normalize(path)
const path = require('path');
const filePath = './examples/file.txt';
console.log('Base name:', path.basename(filePath)); // 'file.txt'
console.log('Directory:', path.dirname(filePath)); // './examples'
console.log('Extension:', path.extname(filePath)); // '.txt'
The os module provides information about the operating system and offers platform-specific functionality.
os.type()
(e.g., 'Linux', 'Windows_NT')os.arch()
os.env
objectconst os = require('os');
console.log('OS Type:', os.type());
console.log('Architecture:', os.arch());
console.log('Platform:', os.platform());
These core modules are essential in real-world applications like file managers, static site generators, and platform-specific tools. For example:
Timers are essential for scheduling tasks in Node.js. They allow you to execute code after a specified delay (setTimeout) or repeatedly at regular intervals (setInterval). These timers work similarly to their browser counterparts but are optimized for server-side use.
// setTimeout example
const delayedMessage = () => {
console.log('This message appears after 2 seconds!');
};
setTimeout(delayedMessage, 2000);
// setInterval example
const repeatedMessage = () => {
console.log('This message repeats every 3 seconds!');
};
setInterval(repeatedMessage, 3000);
Node.js handles I/O operations efficiently using Buffers and Streams. Buffers are used to store binary data while Streams allow for continuous data flow, making them ideal for handling large files or network communication.
// Reading a file with streams
const fs = require('fs');
const readStream = fs.createReadStream('input.txt', { encoding: 'utf8' });
readStream.on('data', (chunk) => {
console.log('Received chunk:', chunk);
});
readStream.on('end', () => {
console.log('End of file reached');
});
// Creating a buffer
const buf = Buffer.alloc(10);
buf.write('Hello');
console.log(buf.toString()); // 'Hello\u0000\u0000\u0000\u0000'
Combining timers with streams and buffers allows you to handle asynchronous operations efficiently. For example, you could use a timer to schedule periodic file reads or buffer processing.
const fs = require('fs');
// Read file every 5 seconds
function readLogFile() {
const readStream = fs.createReadStream('log.txt', { encoding: 'utf8' });
readStream.on('data', (chunk) => {
console.log('Log data:', chunk);
});
}
// Schedule log reading
const logInterval = setInterval(readLogFile, 5000);
// Clear interval after 1 minute
setTimeout(() => {
clearInterval(logInterval);
console.log('Stopped monitoring log file');
}, 60000);
Error handling is an essential part of building robust and reliable Node.js applications. In this chapter, we'll explore various methods for catching and managing errors, as well as tools for debugging your code.
For synchronous operations, Node.js provides the try/catch pattern similar to other programming languages.
try {
// Code that might throw an error
const result = someFunction();
} catch (error) {
// Handle the error
console.error('An error occurred:', error);
}
For asynchronous operations like callbacks or promises, Node.js provides additional patterns for error handling.
fs.readFile('file.txt', (err, data) => {
if (err) {
// Handle error
console.error('Error reading file:', err);
return;
}
// Process the data
});
const readFileAsync = () => {
return new Promise((resolve, reject) => {
fs.readFile('file.txt', (err, data) => {
if (err) reject(err);
else resolve(data);
});
});
}
async function example() {
try {
const data = await readFileAsync();
console.log('File read successfully:', data);
} catch (error) {
console.error('Error in async operation:', error);
}
}
When errors occur, it's important to have the right tools and techniques to debug them effectively.
const processDebugger = require('process').debug;
// Example usage:
try {
// Code that might fail
} catch (error) {
console.error({
error: error.message,
stack: error.stack,
context: {
someVariable: variableValue,
processDebug: processDebugger()
}
});
}
Question 1 of 19