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.

JavaScript
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();
});
Output
/hello -> 200 Hello
/missing -> 404 Not found

Explanation

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.

Explore the dry run
Guided walkthroughReady
1const http = require("node:http");
2
3const server = http.createServer((req, res) => {
4 const known = req.url === "/hello";
5 res.statusCode = known ? 200 : 404;
6 res.setHeader("Content-Type", "text/plain");
7 res.end(known ? "Hello" : "Not found");
8});
9
10server.listen(0, "127.0.0.1", async () => {
11 const base = "http://127.0.0.1:" + server.address().port;
12 for (const path of ["/hello", "/missing"]) {
13 const response = await fetch(base + path);
14 console.log(path + " -> " + response.status + " " + (await response.text()));
15 }
16 server.close();
17 server.closeAllConnections();
18});

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
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput 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

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.

Further reading