An HTTP server without frameworks
An HTTP server in Node.js without frameworks is created using the built-in http module.
Node can already accept and handle HTTP requests out of the box - frameworks like Express just wrap that in a convenient layer.
1. What happens "under the hood"
When you call:
import http from 'node:http';
const server = http.createServer((req, res) => {
res.end('Hello!');
});
server.listen(3000);Node does the following:
- Opens a TCP port (3000) and starts listening for connections.
- Each connection is a socket.
- Based on the HTTP protocol, Node parses the request (method, URL, headers, body) and passes it into the
(req, res)callback. - You build the response (
res.statusCode,res.setHeader,res.end()), and Node sends it back to the client.
So createServer is really just "a handler for all incoming HTTP packets".
2. How Node understands requests
The req (Request) object:
- holds the method (
req.method, GET, POST, etc.), - the path (
req.url), - the headers (
req.headers), - and the body stream (for POST/PUT).
The res (Response) object:
- lets you set the status and headers,
- is a Writable Stream you write the response data into.
3. The simplest way to do routing
Node doesn't give you "routes" out of the box, but you can use req.url and req.method:
if (req.method === 'GET' && req.url === '/users') {
// return the list of users
} else if (req.method === 'POST' && req.url === '/users') {
// create a new user
} else {
// 404
}That lets you describe any logic by hand, without Express.
4. Working with the request body
The body (req) is a Readable Stream.
Node doesn't read it automatically, so you collect the data yourself:
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', () => {
const data = JSON.parse(body);
// process the JSON
});This gives you full control over the data stream, including large files.
5. Returning different kinds of responses
You can set headers by hand:
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: true }));or plain text:
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('Hello, world!');6. Why this can be useful
Building a server without frameworks is worth it if you:
- want to understand how Express works under the hood;
- are writing a very lightweight microservice with no need for extra abstraction;
- need low-level control over streams (streaming, proxying, HTTP/2, SSE, WebSocket upgrade);
- are optimizing performance (removing the middleware layer).
7. What you can add on top
- Routing, via a
Map,switch, regular expressions, or your own parser. - JSON parsing, collecting the body and calling
JSON.parse. - Static files, reading files via
fs.createReadStream()and streaming them. - CORS, adding
Access-Control-Allow-*headers. - Errors/404, returning the right status via
res.statusCode.
8. In short
An HTTP server in Node.js is created by calling
http.createServer(). It receivesreqandresobjects, which represent the HTTP request and response. From there, you decide how to parse the URL, what to return, and which headers to set. Everything else (routing, middleware, a body parser, etc.) is just built on top - the foundation always stays the same.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.