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
- Node filesystem
- Fetch in the browser (contrast client vs server)
Minimal Server
// 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
// 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)
// 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
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
// 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.