A structured guide to http.createServer, request/response handling, Content-Type, query strings, JSON parsing, and CommonJS modules—using a minimal server you can run locally.
May 29, 2026
Node's built-in http module is the smallest useful step from scripts to a real backend: a process that listens on a port and runs your logic for each incoming request. This post walks through a minimal server, the request/response model, headers, query strings, and splitting code into modules—with examples you can run locally.
What you need
Node.js installed (LTS recommended)
Basic familiarity with JavaScript and the terminal
Run the file, open http://localhost:3000/overview, and you should see overview. Other paths return 404.
Two details matter in production-quality handlers:
End every response with res.end() (or res.write() followed by res.end()). An open response leaves the client waiting.
Return after a successful route so later branches (such as 404) do not run for the same request.
Request and response objects
Object
Role
req
Incoming HTTP request: URL, method, headers, and (for many verbs) a readable body stream
res
Outgoing HTTP response: status line, headers, and body
Routing in a small app usually starts with req.url. For anything beyond exact path matching, parse the URL (see Query strings below).
Ports and server.listen
A port is a numbered endpoint on a host (for example localhost:3000). Clients connect to host:port; your server accepts connections on that port.
server.listen(3000, callback) does not block the rest of your program synchronously. Node registers the server with the operating system and keeps the event loop running. When a connection arrives, Node invokes the function passed to http.createServer. That design is what allows a single Node process to handle many concurrent connections.
http.createServer defines what happens per request; listen defines where the process accepts them.
Extra requests: /favicon.ico
Browsers often request /favicon.ico automatically when you load a page. If you only handle explicit routes, you may see unexplained 404s in your logs. You can ignore them or respond explicitly:
if (pathName === "/favicon.ico") {
res.writeHead(204);
res.end();
return;
}
The Content-Type header
The response body is opaque bytes until the client knows how to interpret it. The Content-Type header tells the browser (or API client) whether the body is plain text, HTML, JSON, or something else. Set it when you send a body, typically via res.writeHead:
Invalid JSON throws a SyntaxError. In request handlers, wrap parsing in try/catch and respond with 400 Bad Request when input is malformed.
Query strings
req.url includes the path and the query string (for example /search?q=node&page=2). Comparing req.url to "/search" fails when query parameters are present.
Use the url module to separate pathname and query:
module is provided in every CommonJS file; module.exports defines what require() returns. This pattern keeps the HTTP layer focused on protocol concerns and leaves business logic in dedicated modules.
Summary
Topic
Takeaway
createServer
One callback per incoming request
listen
Registers the port; does not block the entire process
req / res
Read the request; write status, headers, and body
Content-Type
Required for correct rendering and parsing
JSON.parse
String → object; validate and handle errors
url.parse / URL
Split pathname and query parameters
module.exports / require
Structure larger servers across files
From here, typical next steps are reading POST bodies, using a router library, or moving to frameworks such as Express or Fastify. The underlying model—callback per request, headers, and explicit response completion—stays the same.