Building Your First Web Server
This is the final chapter of the tutorial, and it ties everything together. You will use what you learned about running scripts, modules, asynchronous code, and error handling to build something real: a small web server that responds to requests from a browser.
What Is a Web Server?
A web server is a program that waits for requests (asks for a page or some data) and sends back responses (the page or data). When you visit a website, your browser sends a request to a server somewhere, and the server sends back the content.
Node.js is very good at this kind of work because it can handle many connections at once without getting bogged down — the non-blocking model from earlier chapters is exactly what makes this possible.
The Built-In `http` Module
Node.js comes with a module called http that lets you create web servers. A module is a reusable piece of code you can load into your program. You do not need to install anything — it ships with Node.js.
Create a file called server.js:
const http = require('node:http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello, world!');
});
server.listen(3000, () => {
console.log('Server running at http://localhost:3000');
});Run it with node server.js, then open http://localhost:3000 in your browser. You should see "Hello, world!". The word localhost means "this same computer", and 3000 is the port — a numbered doorway the server listens on.
Understanding the Code
Let's walk through each part:
require('node:http')loads the built-in HTTP module, just like you loaded your own modules withrequireearlier.http.createServer(...)creates a server. The function you pass in runs every time a request arrives.reqis the request: what the visitor asked for, including the web address they used.resis the response: what you send back.res.writeHead(200, ...)sends a status code.200means "OK, everything went fine". It also says the content is plain text.res.end(...)sends the body of the response and finishes it. The request is not complete until you callend.server.listen(3000)tells the server to start waiting for connections on port 3000.
Responding to Different Requests
Real servers behave differently depending on what is asked. Here is a version that checks which page was requested:
const http = require('node:http');
const server = http.createServer((req, res) => {
if (req.url === '/') {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('<h1>Home page</h1>');
} else if (req.url === '/about') {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('<h1>About us</h1>');
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Page not found');
}
});
server.listen(3000, () => {
console.log('Server running at http://localhost:3000');
});Here req.url is the part of the address after the domain. If you visit /about, you get the About page. Anything else gets a 404 response, which is the standard code for "not found". Notice the Content-Type is now text/html, so the browser renders the heading properly.
Handling Errors
Servers run for a long time, so you should handle failures gracefully. A common one: port 3000 is already in use by another program.
server.on('error', (err) => {
console.error('Server problem:', err.message);
});Add this listener (a function that runs when an event happens — the event-driven idea from earlier chapters) to print a friendly message instead of crashing with a confusing stack trace.
Stopping the Server
While the server runs, your terminal is busy — it is listening for requests. To stop it, press Ctrl+C in the terminal. To see changes after editing your code, stop the server and start it again.
Where to Go from Here
The http module is low-level: you build everything by hand. Real projects usually use a framework — a set of tools that handles routing, responses, and common tasks for you. The most popular Node.js framework is Express, and everything you learned here is the foundation it builds on. But as you have just seen, behind every framework is the same simple loop: wait for a request, decide what to send, send it.
Exercise
What status code should you send when a visitor asks for a page that does not exist?
Show answer
404, which tells the browser "not found".
Exercise
Why is it a problem if you forget to call res.end() in your request handler?
Show answer
The response never completes, so the browser waits indefinitely for the rest of the reply.
Spotted a mistake, or code that does not work?