CodingNic

Core Node.js Modules

The http Module: A Server With No Framework

Core Node.js Modules 15 min read

The http Module: A Server With No Framework

Objectives

By the end of this lesson, you should be able to:

  • Build a working HTTP server with Node’s built-in http module
  • Route requests manually based on req.url and req.method
  • Send a response with a status code, headers, and a body

💡 Why this matters: Express, starting next module, is a layer on top of exactly this module. Building a server manually first, routing, status codes, headers, response bodies, makes it clear what problem Express actually solves, and why it’s worth using.

⚠️ A note on verification: every snippet and every response shown below was actually run and tested with real HTTP requests.

A Minimal Server

javascript
const http = require('http');

const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Welcome to the homepage');
});

server.listen(3939, () => {
  console.log('server listening on port 3939');
});
text
server listening on port 3939

http.createServer(callback) creates a server, the callback runs once for every incoming request, receiving a request object (req) and a response object (res). server.listen(port, callback) starts the server actually listening for connections on that port. This callback pattern (Module 3’s event loop, and this module’s events) is core to how Node handles I/O, createServer’s callback fires whenever the underlying 'request' event happens.

Routing Manually

javascript
const http = require('http');

const server = http.createServer((req, res) => {
  console.log(`${req.method} ${req.url}`);

  if (req.url === '/' && req.method === 'GET') {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Welcome to the homepage');
  } else if (req.url === '/about' && req.method === 'GET') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ page: 'about', author: 'Erin' }));
  } else {
    res.writeHead(404, { 'Content-Type': 'text/plain' });
    res.end('Not Found');
  }
});

server.listen(3939);

Testing this live with real requests:

bash
curl -i http://localhost:3939/
text
HTTP/1.1 200 OK
Content-Type: text/plain

Welcome to the homepage
bash
curl -i http://localhost:3939/about
text
HTTP/1.1 200 OK
Content-Type: application/json

{"page":"about","author":"Erin"}
bash
curl -i http://localhost:3939/nowhere
text
HTTP/1.1 404 Not Found
Content-Type: text/plain

Not Found

req.url is the path requested, req.method is the HTTP method (GET, POST, and so on). There’s no built-in routing, every route is a manual if/else if check against req.url and req.method, and anything not explicitly matched falls through to a 404. res.writeHead(statusCode, headers) sets the status code and headers, res.end(body) sends the body and finishes the response, nothing sends until .end() is called.

Why a Framework Helps

Even this small example shows the pain points: routing is a manual if/else if chain that gets unwieldy fast, every response needs its headers and status code set by hand, and there’s no built-in way to parse a request body, read URL query parameters cleanly, or share logic across routes. Express, starting next module, solves exactly these problems, but everything it does still ultimately runs on top of this same http module.

Try It

  1. Build a server with three routes, /, /contact, and /products, each returning different plain text.
  2. Add a route that only responds to POST requests to /submit, returning a different message than a GET to the same path.
  3. Test your server with curl -i, confirming both the status code and the response body match what you expect for each route, including an unmatched path.
  4. Explain, in your own words, what res.writeHead() and res.end() each do, and what happens if .end() is never called.

Recap

  • http.createServer(callback) creates a server, the callback fires on every request, server.listen(port) starts it.
  • Routing with the raw http module is manual, checking req.url and req.method directly.
  • res.writeHead(status, headers) sets the response’s status and headers, res.end(body) sends the body and completes the response.

Next lesson: url, parsing and working with URLs and query parameters.