Building an HTTP server
A server with no framework, routing by hand, reading the body, and when to reach for Express-style libraries.
The raw server
import http from "node:http";
const server = http.createServer(async (req, res) => {
const url = new URL(req.url, "http://" + req.headers.host);
if (req.method === "GET" && url.pathname === "/health") {
res.writeHead(200, { "content-type": "application/json" });
res.end(JSON.stringify({ ok: true }));
return;
}
if (req.method === "POST" && url.pathname === "/echo") {
let body = "";
for await (const chunk of req) body += chunk;
res.writeHead(200, { "content-type": "application/json" });
res.end(body || "{}");
return;
}
res.writeHead(404, { "content-type": "application/json" });
res.end(JSON.stringify({ error: "not found" }));
});
server.listen(process.env.PORT ?? 3000, () => console.log("listening"));⚠️
Every request body must be size-limited. Without a cap, a single client can stream gigabytes into memory and take the process down.
Framework or no framework
| Use case | Reach for |
|---|---|
| A handful of endpoints, learning | The built-in node:http |
| Typical REST API | Express, Fastify, Hono, Koa |
| Full-stack with SSR | Next.js, Nuxt, Remix |
| Static files only | A CDN or static host — no server needed |
// the same idea in Express
import express from "express";
const app = express();
app.use(express.json({ limit: "100kb" }));
app.get("/health", (req, res) => res.json({ ok: true }));
app.post("/echo", (req, res) => res.json(req.body));
app.listen(3000);Production concerns
- Handle
unhandledRejectionanduncaughtExceptionby logging and exiting — a corrupted process should restart, not linger. - Add a graceful shutdown on
SIGTERM: stop accepting connections, finish in-flight requests, then exit. - Put the app behind a reverse proxy or platform (Cloudflare, nginx) for TLS, compression and rate limiting.
- Health endpoints let load balancers remove a bad instance automatically.
💡
Node's cluster mode or a process manager lets you use every CPU core. With container platforms you usually scale by running more containers instead.
FAQ
Which Node version should I target?
The current LTS. It receives security fixes and matches what most deployment platforms default to.
Do I need a framework?
Not for a handful of routes. Adopt one when you need routing, middleware, validation and error handling without reinventing them.
Related
Files and paths in Node HTTP methods
Last refreshed 2026-09-17.