Node.js HTTP Server in JavaScript

Introduction

Node’s built-in http module creates HTTP servers without external frameworks. Understanding raw request/response handling clarifies how Express, Fastify, and Next.js build on the same primitives. This chapter serves a simple API and parses URLs.

Prerequisites

Minimal Server

javascript
// server.mjs
import http from "node:http";
 
const server = http.createServer((req, res) => {
  res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
  res.end("Hello from Node\n");
});
 
const PORT = 3000;
server.listen(PORT, () => {
  console.log(`http://127.0.0.1:${PORT}`);
});

Run node server.mjs and visit the URL in a browser or curl.

Routing by URL

javascript
// routes.mjs
import http from "node:http";
 
const server = http.createServer((req, res) => {
  const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
 
  if (url.pathname === "/health") {
    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(JSON.stringify({ ok: true }));
    return;
  }
 
  if (url.pathname === "/") {
    res.writeHead(200, { "Content-Type": "text/html" });
    res.end("<h1>Home</h1>");
    return;
  }
 
  res.writeHead(404, { "Content-Type": "text/plain" });
  res.end("Not Found\n");
});
 
server.listen(3000);

Read Request Method and Body (POST)

javascript
// post.mjs
import http from "node:http";
 
function readBody(req) {
  return new Promise((resolve, reject) => {
    let data = "";
    req.on("data", (chunk) => (data += chunk));
    req.on("end", () => resolve(data));
    req.on("error", reject);
  });
}
 
const server = http.createServer(async (req, res) => {
  if (req.method === "POST" && req.url === "/echo") {
    const body = await readBody(req);
    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(JSON.stringify({ received: body }));
    return;
  }
 
  res.writeHead(405);
  res.end("Method Not Allowed\n");
});
 
server.listen(3000);

Frameworks handle streaming and size limits for you.

JSON API Response Helper

javascript
function sendJson(res, status, data) {
  res.writeHead(status, { "Content-Type": "application/json" });
  res.end(JSON.stringify(data));
}

When to Use a Framework

Raw http is great for learning. Production APIs often use Express, Fastify, or Hono for routing, middleware, and security headers.

Mini Example: In-Memory Todos API

javascript
// todos-api.mjs
import http from "node:http";
 
let todos = [{ id: 1, title: "Learn Node" }];
 
const server = http.createServer((req, res) => {
  const url = new URL(req.url ?? "/", "http://localhost");
 
  if (req.method === "GET" && url.pathname === "/todos") {
    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(JSON.stringify(todos));
    return;
  }
 
  res.writeHead(404);
  res.end();
});
 
server.listen(3000, () => console.log("todos on :3000"));

Test: curl http://127.0.0.1:3000/todos

FAQ

HTTP vs HTTPS?

https module adds TLS certificates—terminate TLS at a reverse proxy (nginx) in many deployments.

Node fetch?

Available globally in current Node LTS for outbound requests; http is for inbound servers.

WebSockets?

Separate ws library or http.Server upgrade—beyond this intro.

What comes next?

Async and reliability in Node.