How to build a web server from scratch using only Node.js—without Express or any framework.
Every web service starts as a Node.js HTTP server. It:
- Listens for requests from clients
- Parses the incoming data
- Runs some logic
- Sends back a response
This step teaches you exactly how each of these works.
An HTTP server that:
- Accepts POST /tasks requests with JSON data
- Returns GET /tasks to list all tasks
- Uses manual routing (no framework helps you)
- Parses request bodies from stream chunks
- Sends proper HTTP status codes
Frameworks like Express do all this automatically. But when things go wrong in production:
- You need to debug the lowest level
- You need to understand how bodies are parsed
- You need to know why a request might hang
Knowing the raw server means you can diagnose any issue.
All routing lives in server.js at the top. Look for the createServer() function.
Key parts:
http.createServer(async (req, res) => {...})— creates the serverreq.urlandreq.method— tells you what request arrivedreq.on('data', chunk => {...})— receives body in partsres.writeHead(200, { 'Content-Type': 'application/json' })— sets response headersres.end(JSON.stringify(data))— sends the response
When a client sends data (like POST with JSON), it arrives in chunks, not all at once.
Think of it like mail:
- Chunk 1: envelope arrives
- Chunk 2: first page of letter
- Chunk 3: second page
- Chunk 4: signature
Buffer all chunks, wait for end, then parse.
Example flow:
let body = "";
req.on("data", (chunk) => {
body += chunk; // Accumulate pieces
});
req.on("end", () => {
const data = JSON.parse(body); // Parse when complete
});If you parse before end fires, you get incomplete data. ✗
❌ Mistake 1: Assuming body arrives all at once
✓ Fix: Always listen to data and end events
❌ Mistake 2: Not handling parse errors
✓ Fix: Wrap JSON.parse in try/catch
❌ Mistake 3: Wrong response headers
✓ Fix: Always set Content-Type: application/json
❌ Mistake 4: Updating request body stream without buffering
✓ Fix: Buffer everything first, parse second
Task: Add a new endpoint GET /health
Requirements:
- Return
{ status: "ok", uptime: process.uptime() } - Status code: 200
- Content-Type: application/json
- Do NOT use Express or any external library
Hints:
- Add a new
ifstatement checkingreq.urlandreq.method - Use
res.writeHead()to set headers - Use
res.end()to send the response
Test it:
curl http://localhost:3000/healthProduction incidents often trace back to:
- Wrong status code sent (500 instead of 400)
- Malformed response JSON
- Not handling network disconnections
- Request body timeouts
Understanding raw servers helps you:
- Spot issues faster
- Design better error pages
- Handle edge cases in production
The server is just infrastructure. Next, you'll learn how to store and retrieve data using a Hash Map.