Building a Server
A Node.js HTTP server is a long-running program created with http.createServer that listens on a port and runs a handler function for every incoming request, sending back a response you control.
Learn Building a Server in our free Node.js course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Node.js course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
In the last lesson you met the http module. Now you'll use it to build a real server: one that starts up, listens on a port, reads what the client asked for, and sends back exactly the status, headers, and body you choose.
What You'll Learn in This Lesson
1️⃣ The Simplest Possible Server
A working Node server is only a few lines. You give http.createServer a handler function , then call server.listen(port) to start it. The handler receives two objects every time a request arrives: req (what the client asked for) and res (the reply you build). Read every comment below, then run it.
Save that as server.js and start it. Unlike a normal script, it doesn't exit — it stays running, waiting for requests, until you stop it with Ctrl+C .
2️⃣ Setting the Status Code and Headers
Every response carries a status code (200 means OK, 404 means not found) and headers (metadata about the body). You can set both in one shot with res.writeHead(status, headers) , which must run before res.end .
Or set them one at a time with res.statusCode and res.setHeader(key, value) . To send JSON , turn your object into a string with JSON.stringify and set Content-Type to application/json so the client knows how to read it.
3️⃣ Reading the Incoming Request
So far we've ignored what the client actually asked for. The req object tells you: req.method is the verb ( GET , POST , …) and req.url is the path. A request body (sent with a POST) doesn't arrive all at once — it streams in as chunks, so you collect it on the 'data' event and act on it when 'end' fires.
Your turn. The handler below is almost done — fill in the three blanks marked ___ so it replies with the JSON object {message:'ok'} . Follow the 👉 hints, then run it and compare with the expected output.
Mini-Challenge: A Two-Route Server
No blanks this time — just a brief and an outline. Using only req.url and if statements (no router yet — that's the next lesson), build a server that answers two paths and 404s everything else. Write it yourself, run it, and check your output against the example in the comments.
Common Errors (and the fix)
- The request hangs forever — You almost certainly forgot res.end() . Without it Node never finishes the response, so the browser or curl just spins. Every code path through your handler must call res.end(...) exactly once.
- "EADDRINUSE: address already in use :::3000" — Another program (often an old copy of this server you forgot to stop) is already holding port 3000. Stop the other process, pick a different port, or find and kill it ( lsof -i :3000 on macOS/Linux).
- "Cannot set headers after they are sent to the client" — You called res.setHeader or res.writeHead after res.end (or called res.end twice). Set the status and all headers first, then end the response last.
- JSON shows up as plain text (or won't parse) — You sent JSON.stringify(obj) but forgot the header. Add res.setHeader('Content-Type', 'application/json') so the client treats it as JSON.
- The server "won't stop" — That's normal: a server is meant to keep running. Press Ctrl+C in the terminal where it's running to shut it down.
Pro Tips
- 💡 Read the port from the environment: use const port = process.env.PORT || 3000 so hosting platforms can choose the port for you.
- 💡 Always send a Content-Type: it tells clients how to read your body. text/plain , text/html , and application/json cover most early cases.
- 💡 Frameworks build on this: Express and friends are thin layers over exactly this req / res model — learning the raw version makes them click instantly.
📋 Quick Reference — Node.js Servers
Concept
Node Syntax
Create a server
http.createServer(fn)
Start listening
server.listen(3000)
Status + headers
res.writeHead(200, headers)
Set one header
res.setHeader(k, v)
Send body + close
res.end(body)
Request method
req.method
Request path
req.url
Frequently Asked Questions
🎉 Lesson Complete!
- ✅ http.createServer(fn) builds a server; the handler runs once per request
- ✅ server.listen(port) starts it — and keeps it running until Ctrl+C
- ✅ res.writeHead(status, headers) or res.statusCode + res.setHeader shape the reply
- ✅ Send JSON with JSON.stringify and a application/json Content-Type
- ✅ req.method and req.url tell you what was asked; a POST body streams in via 'data' / 'end'
- ✅ Always finish with res.end() or the request hangs
- ✅ Next lesson: Routing by Hand — send different responses for different URLs
Practice quiz
Which built-in module creates an HTTP server in Node?
- http
- fs
- net-server
- express
Answer: http. The built-in http module's createServer builds a server with no install needed.
How many handler functions does http.createServer take?
- One handler, called for every request
- One per route
- Two: request and response handlers
- None
Answer: One handler, called for every request. createServer takes a single handler that Node calls for every incoming request.
What starts the server actually listening for requests?
- server.start()
- server.open()
- server.listen(port)
- server.run()
Answer: server.listen(port). Nothing happens until server.listen(port) begins listening on a port.
What does res.end() do?
- Restarts the server
- Logs the request
- Opens a new connection
- Sends the body and closes the response
Answer: Sends the body and closes the response. res.end() sends the body and finishes the response; without it the request hangs.
Which call sets the status code and headers together, before res.end?
- res.writeHead(status, headers)
- res.send(status)
- res.header(status)
- res.status(headers)
Answer: res.writeHead(status, headers). res.writeHead(statusCode, headersObject) sets both at once and must run before res.end.
To send a JS object as JSON, what must you call first?
- JSON.parse(obj)
- JSON.stringify(obj)
- obj.toJSON only
- res.json(obj)
Answer: JSON.stringify(obj). res.end sends strings, so JSON.stringify converts the object into a JSON string.
Which property tells you the HTTP verb of the request?
- req.url
- req.verb
- req.method
- req.path
Answer: req.method. req.method holds the verb such as GET or POST; req.url holds the path.
How does a POST request body arrive in the raw http handler?
- All at once on req.body
- As chunks via 'data' events, finalized on 'end'
- As a query string
- In req.params
Answer: As chunks via 'data' events, finalized on 'end'. The body streams in as chunks you collect on 'data', then act on when 'end' fires.
What does the error 'EADDRINUSE :::3000' mean?
- The handler crashed
- JSON failed to parse
- Another program already holds port 3000
- The body was too large
Answer: Another program already holds port 3000. EADDRINUSE means the port is already in use, often by an old copy of the server.
What header tells the client a response body is JSON?
- Content-Type: application/json
- Accept: text/plain
- Content-Length
- X-JSON: true
Answer: Content-Type: application/json. Setting Content-Type to application/json tells clients to parse the body as JSON.
Continue this course
- Previous: The http Module
- Next: Routing by Hand