Difficulty: Beginner
How do you create an HTTP server in Node.js without any framework? Explain the request and response objects.
The http module is Node.js's built-in module for creating HTTP servers and making HTTP requests. http.createServer() takes a callback with request (IncomingMessage) and response (ServerResponse) objects.
The request object contains information about the incoming request: URL, method, headers, and the request body (which must be read as a stream). The response object is used to send data back: set status codes, headers, and write the response body.
Understanding the raw http module is important even if you use Express or other frameworks, because they are built on top of it. Express's req and res are enhanced versions of the native objects.
The http module also provides http.request() and http.get() for making outgoing HTTP requests, though most developers now use the built-in fetch() API (Node 18+) or libraries like axios for this purpose.
const http = require('http');
const server = http.createServer((req, res) => {
// Request info
console.log(`${req.method} ${req.url}`);
console.log('Headers:', req.headers);
// Simple routing
if (req.method === 'GET' && req.url === '/') {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('<h1>Home Page</h1>');
} else if (req.method === 'GET' && req.url === '/api/health') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ status: 'ok', uptime: process.uptime() }));
} else {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Not found' }));
}
});
server.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
createServer callback runs for every request. writeHead sets status and headers. end() sends the response body and finishes the response.
const http = require('http');
const server = http.createServer((req, res) => {
if (req.method === 'POST' && req.url === '/api/users') {
let body = '';
// Request body comes as a stream
req.on('data', (chunk) => {
body += chunk.toString();
});
req.on('end', () => {
try {
const user = JSON.parse(body);
console.log('Received:', user);
res.writeHead(201, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
message: 'User created',
data: { id: 1, ...user }
}));
} catch (err) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Invalid JSON' }));
}
});
}
});
server.listen(3000);
The request body must be collected from the stream using data/end events. This is what Express's express.json() middleware does internally.
// Modern approach: fetch (Node 18+)
async function fetchData() {
const response = await fetch('https://api.example.com/users');
const data = await response.json();
console.log(data);
}
// Using http module (lower level)
const http = require('http');
http.get('http://api.example.com/users', (res) => {
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => {
console.log(JSON.parse(data));
});
}).on('error', (err) => {
console.error('Request failed:', err.message);
});
// POST request with http module
const options = {
hostname: 'api.example.com',
path: '/users',
method: 'POST',
headers: { 'Content-Type': 'application/json' }
};
const req = http.request(options, (res) => {
// handle response
});
req.write(JSON.stringify({ name: 'John' }));
req.end();
fetch() is the modern approach for HTTP clients in Node 18+. The http module works for older versions but requires manual stream handling.
http module, createServer, request, response, routing