The Journey of a Web Request: Mastering HTTP with Node.js


Picture this, You're sitting at your computer, curious about a new article you saw shared on social media. You type the URL into your browser and hit enter. Within seconds, the webpage loads perfectly on your screen. It seems simple, right? But behind this everyday action lies one of the most fascinating processes in web development—the HTTP request-response cycle.
As developers, we often take this process for granted, but understanding what happens between that "enter" keypress and the webpage appearing is crucial for building better web applications. Today, we'll explore this journey step by step and learn how to harness Node.js's powerful http
module to create our own servers and clients.
What Really Happens When You Hit Enter?
Every time you browse the web, you're participating in a carefully orchestrated dance between your browser (the client) and a server somewhere on the internet.
The Behind-the-Scenes Journey
Step 1: DNS Resolution
When you type https://hiteshchoudhary.com
, your browser doesn't actually know where to find that website. It needs to convert this human-readable domain into an IP address that computers understand—something like 76.76.21.22
. This conversion happens through DNS servers, which act like the internet's phone book.
Step 2: TCP Connection Once your browser has the IP address, it establishes a connection with the server through TCP (Transmission Control Protocol). Think of it as dialing a phone number and waiting for someone to pick up. This usually happens on port 80 for HTTP or port 443 for HTTPS.
Step 3: HTTP Request Your browser creates an HTTP request containing:
- HTTP Method: Usually GET when visiting a webpage
- Headers: Information about your browser and accepted content types
- URL Path: The specific page you're requesting
- Body: Any data you're sending (mainly for form submissions)
Step 4: Server Processing The server receives your request and processes it—fetching data from databases, running business logic, or simply serving static files.
Step 5: HTTP Response The server sends back a response with:
- Status Code: 200 for success, 404 for "not found", etc.
- Headers: Content type, caching rules, and metadata
- Body: The actual content (HTML, JSON, images)
Step 6: Client Rendering Your browser processes the response, renders HTML, applies CSS, executes JavaScript, and displays the complete webpage.
Building Your First HTTP Server with Node.js
Now let's get our hands dirty with code! Node.js comes with a built-in http
module that makes creating web servers surprisingly straightforward.
Starting Simple
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello, World! This is my first Node.js server!');
});
server.listen(3000, () => {
console.log('🚀 Server running at http://localhost:3000');
});
Congratulations—you've just created a web server! The createServer()
method takes a callback function that receives the incoming request (req
) and the response object (res
) we'll send back.
Creating a More Realistic Server
Real websites need to handle different pages and routes:
const http = require('http');
const server = http.createServer((req, res) => {
const { method, url } = req;
if (url === '/') {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(`
<html>
<body>
<h1>Welcome to My Website!</h1>
<p>Check out our <a href="/about">About page</a></p>
<p>Or visit our <a href="/api/users">API</a></p>
</body>
</html>
`);
} else if (url === '/about') {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(`
<html>
<body>
<h1>About Us</h1>
<p>We're building awesome web applications with Node.js!</p>
<a href="/">Back to Home</a>
</body>
</html>
`);
} else if (url === '/api/users') {
const users = [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' }
];
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ users, total: users.length }));
} else {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('<h1>404 - Page Not Found</h1>');
}
});
server.listen(3000);
This server now handles multiple routes and even serves JSON data for an API endpoint!
Handling Query Parameters
Web applications often need to process query parameters—those key-value pairs that come after the ?
in a URL:
const http = require('http');
const url = require('url');
const server = http.createServer((req, res) => {
const parsedUrl = url.parse(req.url, true);
const { pathname, query } = parsedUrl;
if (pathname === '/greet') {
const name = query.name || 'Guest';
const age = query.age || 'unknown';
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
greeting: `Hello, ${name}!`,
age: age,
message: 'Thanks for visiting!'
}));
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Page not found');
}
});
server.listen(3000, () => {
console.log('Try: http://localhost:3000/greet?name=John&age=25');
});
Making HTTP Requests: Node.js as a Client
Node.js isn't just great for creating servers—it's also excellent for making HTTP requests to other services.
Making GET Requests
const http = require('http');
function fetchUserData(userId) {
const url = `http://jsonplaceholder.typicode.com/users/${userId}`;
http.get(url, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
try {
const user = JSON.parse(data);
console.log(`Name: ${user.name}`);
console.log(`Email: ${user.email}`);
} catch (error) {
console.error('Error parsing response:', error.message);
}
});
}).on('error', (error) => {
console.error('Request failed:', error.message);
});
}
fetchUserData(1);
Making POST Requests
const http = require('http');
function createPost(title, body, userId) {
const postData = JSON.stringify({
title: title,
body: body,
userId: userId
});
const options = {
hostname: 'jsonplaceholder.typicode.com',
path: '/posts',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(postData)
}
};
const req = http.request(options, (res) => {
let responseData = '';
res.on('data', (chunk) => {
responseData += chunk;
});
res.on('end', () => {
console.log('Post created!', JSON.parse(responseData));
});
});
req.on('error', (error) => {
console.error('Error:', error.message);
});
req.write(postData);
req.end();
}
Building a Complete Example: Simple Blog API
Let's put everything together with a practical example:
const http = require('http');
const url = require('url');
// In-memory storage
let posts = [
{ id: 1, title: 'Getting Started with Node.js', content: 'Node.js is amazing!', author: 'Sourav' },
{ id: 2, title: 'Understanding HTTP', content: 'HTTP powers the web!', author: 'Maji' }
];
let nextId = 3;
const server = http.createServer((req, res) => {
const { method, url: reqUrl } = req;
const parsedUrl = url.parse(reqUrl, true);
const { pathname } = parsedUrl;
// Set CORS headers
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Content-Type', 'application/json');
if (method === 'GET' && pathname === '/api/posts') {
// Get all posts
res.writeHead(200);
res.end(JSON.stringify({ posts, total: posts.length }));
} else if (method === 'GET' && pathname.startsWith('/api/posts/')) {
// Get specific post
const id = parseInt(pathname.split('/')[3]);
const post = posts.find(p => p.id === id);
if (post) {
res.writeHead(200);
res.end(JSON.stringify(post));
} else {
res.writeHead(404);
res.end(JSON.stringify({ error: 'Post not found' }));
}
} else if (method === 'POST' && pathname === '/api/posts') {
// Create new post
let body = '';
req.on('data', chunk => {
body += chunk.toString();
});
req.on('end', () => {
try {
const { title, content, author } = JSON.parse(body);
if (!title || !content || !author) {
res.writeHead(400);
res.end(JSON.stringify({ error: 'Missing required fields' }));
return;
}
const newPost = {
id: nextId++,
title,
content,
author,
createdAt: new Date().toISOString()
};
posts.push(newPost);
res.writeHead(201);
res.end(JSON.stringify(newPost));
} catch (error) {
res.writeHead(400);
res.end(JSON.stringify({ error: 'Invalid JSON' }));
}
});
} else {
res.writeHead(404);
res.end(JSON.stringify({ error: 'Endpoint not found' }));
}
});
server.listen(3000, () => {
console.log('🚀 Blog API running at http://localhost:3000');
console.log('📖 Endpoints:');
console.log(' GET /api/posts - Get all posts');
console.log(' GET /api/posts/1 - Get specific post');
console.log(' POST /api/posts - Create new post');
});
Essential HTTP Status Codes
Understanding status codes is crucial for building proper APIs:
Code | Meaning | Usage |
200 | OK | Successful request |
201 | Created | Resource successfully created |
400 | Bad Request | Invalid request data |
404 | Not Found | Resource doesn't exist |
500 | Server Error | Something went wrong |
When Should You Use the HTTP Module?
Use Node.js http
module when:
- Building simple microservices or APIs
- Performance is absolutely critical
- You want to understand HTTP fundamentals
- You need fine-grained control
Use frameworks like Express.js when:
- Building complex web applications
- You want built-in routing and middleware
- Development speed is prioritized
- You need features like session management
Wrapping Up
Understanding the HTTP request-response cycle and mastering Node.js's http
module gives you superpowers as a web developer. Every time you see a webpage load, you now understand the intricate dance happening behind the scenes—from DNS resolution to the final response rendering.
The examples we've covered today demonstrate the real power of Node.js for web development. Whether you're building microservices, APIs, or full-scale applications, these fundamentals will serve you well.
Remember, while frameworks like Express.js often make development faster, knowing how things work at the HTTP level makes you a more effective developer. You'll be better at debugging issues, optimizing performance, and building exactly what your application needs.
So go ahead—experiment with these examples, build your own servers, and have fun exploring the fascinating world of HTTP and Node.js! 🚀
Subscribe to my newsletter
Read articles from Sourav Maji directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by