devnotes.

The HTTP Module, In Depth

You've built a basic server with http.createServer() already, back in the Getting Started chapter. This page goes further — handling different routes and methods, reading a request body, and setting proper headers.

Reading the method and path to build simple routing

const http = require('http');

http.createServer((req, res) => {
  if (req.method === 'GET' && req.url === '/') {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Welcome home');
  } else if (req.method === 'GET' && req.url === '/about') {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('About this site');
  } else {
    res.writeHead(404, { 'Content-Type': 'text/plain' });
    res.end('Page not found');
  }
}).listen(3000, () => console.log('Server on port 3000'));

This is exactly the kind of manual routing that frameworks like Express exist to simplify, as covered in the earlier Frameworks page — but seeing it built by hand once makes clear what a framework is actually doing under the hood.

Reading request headers

http.createServer((req, res) => {
  console.log('User agent:', req.headers['user-agent']);
  console.log('Content type:', req.headers['content-type']);
  res.end('Headers logged');
}).listen(3000);

Reading a POST request body

Unlike a framework, raw http doesn't parse the request body for you — the body arrives as a stream of chunks you collect yourself:

http.createServer((req, res) => {
  if (req.method === 'POST') {
    let body = '';
    req.on('data', (chunk) => { body += chunk; });
    req.on('end', () => {
      const parsed = JSON.parse(body);
      res.writeHead(201, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ received: parsed }));
    });
  }
}).listen(3000);

Setting response status and headers correctly

res.writeHead(201, {
  'Content-Type': 'application/json',
  'X-Powered-By': 'my-node-app'
});
res.end(JSON.stringify({ created: true }));

Common status codes worth knowing: 200 (success), 201 (created), 400 (bad request from the client), 401 (not authenticated), 404 (not found), 500 (server error).

Making an outgoing HTTP request

http isn't just for building servers — it also makes requests to other servers:

const http = require('http');

http.get('http://api.example.com/data', (res) => {
  let data = '';
  res.on('data', (chunk) => { data += chunk; });
  res.on('end', () => {
    console.log(JSON.parse(data));
  });
});

In practice, most projects use axios or the built-in fetch (available in modern Node.js) for outgoing requests instead, since they're considerably less verbose than manually collecting chunks — but knowing the raw mechanism underneath explains what those libraries are doing for you.

HTTP vs HTTPS

http handles plain, unencrypted connections. For encrypted traffic, the near-identical https module is used instead — same API shape, but requiring an SSL/TLS certificate, covered on the TLS/SSL page later in this chapter. In production, nearly every public-facing server uses HTTPS.

The HTTP Module, In Depth | DevNotes