The http module: a first server
A web server is a program that waits for requests from browsers and sends responses. In Node.js it takes a few lines with the built-in node:http module. Frameworks like Express are built on top of this module, so once you understand it you will know what they do for you.
The simplest server
Create the file src/server.js:
// src/server.js
import http from 'node:http';
const server = http.createServer((req, res) => {
res.end('Hello from the blog!');
});
server.listen(3000, () => {
console.log('Server running: http://localhost:3000');
});Run it with node src/server.js and open http://localhost:3000 in the browser — the text "Hello from the blog!" appears. The program does not exit: it waits for requests until you press Ctrl+C.
Request and response
The function passed to createServer() is called for every request. It receives two objects:
| Object | What it is | Key parts |
|---|---|---|
req | The request from the browser | req.method, req.url, req.headers |
res | The response you build | res.statusCode, res.setHeader(), res.end() |
res.end() sends the response body and finishes the response. Without this call the browser waits forever.
Status code and headers
Before the body, the status code and headers are sent. The Content-Type header tells the browser what it received:
// src/server.js
import http from 'node:http';
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end('<h1>Notes by Anna</h1><p>Posts are coming soon.</p>');
});
server.listen(3000);Now the browser shows a heading and a paragraph instead of text with tags. charset=utf-8 makes sure non-Latin characters are not garbled.
What came in the request
Print the method and address of each request to the terminal — the simplest server log:
// src/server.js
import http from 'node:http';
const server = http.createServer((req, res) => {
console.log(req.method, req.url);
res.end('ok');
});
server.listen(3000);Open http://localhost:3000/posts?page=2 — the terminal shows GET /posts?page=2. The browser may also send a second request, GET /favicon.ico, for the site icon.
The port from the environment
On a server the port is set with an environment variable, so the number is not hard-coded:
// src/server.js
const PORT = Number(process.env.PORT ?? 3000);
server.listen(PORT, () => console.log(`http://localhost:${PORT}`));Status codes
| Code | Meaning |
|---|---|
200 | Everything is fine |
201 | Created |
400 | Bad request |
404 | Not found |
500 | Server error |
Common mistake
If the port is already taken — for example, the server is running in a second terminal window — the program fails:
Error: listen EADDRINUSE: address already in use :::3000Stop the old server with Ctrl+C or start the new one on another port: PORT=3001 node src/server.js.