Skip to main content

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:

javascript
import http from 'node:http'; const server = http.createServer((req, res) => { res.end('Hello!'); }); server.listen(3000);

Node does the following:

  1. Opens a TCP port (3000) and starts listening for connections.
  2. Each connection is a socket.
  3. Based on the HTTP protocol, Node parses the request (method, URL, headers, body) and passes it into the (req, res) callback.
  4. 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:

javascript
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:

javascript
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:

javascript
res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ ok: true }));

or plain text:

javascript
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 receives req and res objects, 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 ready
Premium

A concise answer to help you respond confidently on this topic during an interview.