JavaScript: an HTTP server and client on the loopback interface
Start a Node.js HTTP server on a free port and request it with fetch to see status codes.
On this page
JavaScript async functions and callbacks.
- Start a Node.js HTTP server on a free port and request it with fetch to see status codes.
- Predict the sample output and explain the state changes.
- Complete the practice task and check the boundary cases.
The concept
HTTP is a text-based request/response protocol on top of TCP. A request has a method, a path, headers, and an optional body. A response has a status code, headers, and a body. Status codes group by meaning: 2xx success, 3xx redirects, 4xx client errors such as 404 Not Found, and 5xx server errors.
The Node.js http module creates a server whose callback receives the request and response objects. Passing port 0 asks the operating system for a free port, and server.address().port tells you which. fetch, built into Node 18 and later, is the client. Binding to 127.0.0.1 keeps the whole conversation on the local machine.
Code example
const http = require("node:http");
const server = http.createServer((req, res) => {
const known = req.url === "/hello";
res.statusCode = known ? 200 : 404;
res.setHeader("Content-Type", "text/plain");
res.end(known ? "Hello" : "Not found");
});
server.listen(0, "127.0.0.1", async () => {
const base = "http://127.0.0.1:" + server.address().port;
for (const path of ["/hello", "/missing"]) {
const response = await fetch(base + path);
console.log(path + " -> " + response.status + " " + (await response.text()));
}
server.close();
server.closeAllConnections();
});/hello -> 200 Hello /missing -> 404 Not found
Follow the code
Open the walkthrough to inspect the example one step at a time.
Show guided walkthrough
const http = require("node:http"); const server = http.createServer((req, res) => { const known = req.url === "/hello"; res.statusCode = known ? 200 : 404; res.setHeader("Content-Type", "text/plain"); res.end(known ? "Hello" : "Not found");}); server.listen(0, "127.0.0.1", async () => { const base = "http://127.0.0.1:" + server.address().port; for (const path of ["/hello", "/missing"]) { const response = await fetch(base + path); console.log(path + " -> " + response.status + " " + (await response.text())); } server.close(); server.closeAllConnections();});Follow the example
Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.
View the complete dry-run table
| Step / line | What happens | State after step | Output so far |
|---|---|---|---|
| The server inspects the request path. | — | — | |
| The status line says whether the request succeeded. | 200 for /hello, 404 otherwise | — | |
| The client sends a GET request over a TCP connection to the local server. | 200 for /hello, 404 otherwise | — | |
| Each response shows the status code and body. | 200 for /hello, 404 otherwise | /hello -> 200 Hello /missing -> 404 Not found | |
| Close the listening socket and idle keep-alive connections so the process can exit. | 200 for /hello, 404 otherwise | /hello -> 200 Hello /missing -> 404 Not found |
Behind the scenes
How JavaScript processes this code
A JavaScript engine parses and runs the source. A function body runs when the function is called. console.log writes to the console.
Cost and common mistakes
O(m) for a message of m bytes, plus connection setup.
- fetch does not reject on a 404 or 500. It rejects only on network failure, so always check response.ok or response.status.
- Never put user-supplied text into a response without encoding it for its context; see the lesson on escaping HTML.
- Binding a server to 0.0.0.0 exposes it to other machines. Use 127.0.0.1 while learning.
Try it yourself
Which status code would signal a server error?
Reveal answer and reasoning
500 Internal Server Error, or another code in the 5xx range.