Skip to main content
Node.js Basics
CHAPTER 10 Beginner

Basic Web Server Routing

Updated: May 13, 2026
20 min read

# Creating a Basic Web Server

Welcome to Chapter 10! In the previous chapter, we built a server that responded to every single request with the exact same message. But real websites have multiple pages—a home page, an about page, a contact page.

How do we tell our server to send different data depending on where the user clicked? The answer is Routing, and we will learn how to build a router from scratch in this chapter.

---

1. Introduction

Routing is the process of determining what the server should do based on the client's request. It relies primarily on two things:

  1. 1. The URL Path: (/about, /contact, /users)
  1. 2. The HTTP Method: (GET, POST, PUT, DELETE)

Right now, we will focus solely on GET requests (which is what happens when you type a URL into a browser and hit enter). By inspecting the req.url property, we can use simple if/else logic to serve different HTML files to the user.

---

2. Learning Objectives

By the end of this chapter, you will be able to:

  • Understand the concept of backend routing.
  • Inspect req.url to serve different responses.
  • Combine the http, fs, and path modules to serve physical HTML files.
  • Handle 404 errors when a user visits a route that doesn't exist.
  • Build a multi-page personal website server.

---

3. Beginner-Friendly Explanations

What is a Route?

Imagine a receptionist at a large office building.
  • Visitor: "I need the Sales department." (URL: /sales)
  • Receptionist (Router): "Take the elevator to the 2nd floor."
  • Visitor: "I need the HR department." (URL: /hr)
  • Receptionist (Router): "Take the elevator to the 5th floor."

If the visitor asks for the "Magic department" (/magic), the receptionist says, "Sorry, that doesn't exist here" (A 404 Not Found error).

In our Node.js server, the callback function inside createServer acts as the receptionist.

---

4. Syntax Explanation

Let's look at how to build a basic router using an if/else block.

```javascript id="ch10-syntax-1" const http = require('http');

const server = http.createServer((req, res) => { // 1. Inspect the requested URL const url = req.url;

// 2. Route the request if (url === '/') { res.writeHead(200, {'Content-Type': 'text/html'}); res.end('<h1>Home Page</h1>'); } else if (url === '/about') { res.writeHead(200, {'Content-Type': 'text/html'}); res.end('<h1>About Us</h1>'); } else { // 3. Handle 404 Not Found res.writeHead(404, {'Content-Type': 'text/html'}); res.end('<h1>404 - Page Not Found!</h1>'); } });

server.listen(3000);

12345678910111213141516171819
**Output Explanation:**
- Visiting `http://localhost:3000/` triggers the first block.
- Visiting `http://localhost:3000/about` triggers the second block.
- Visiting `http://localhost:3000/pizza` triggers the `else` block (404 error).

---

## 5. Real-world Examples

**Serving Real Files**
Writing `<h1>` tags directly inside strings inside `res.end()` is terrible practice. A real HTML page has hundreds of lines of code. Instead, we use the `fs` (File System) module to read a `.html` file from our hard drive, and then we send the contents of that file through `res.end()`.

---

## 6. Multiple Code Examples

### Example 1: Creating the HTML files
Imagine we have two files in our project folder: `index.html` and `about.html`.

html id="ch10-html-1" <!-- index.html --> <html><body><h1>Welcome to the Home Page</h1></body></html>

1

html id="ch10-html-2" <!-- about.html --> <html><body><h1>About Our Company</h1></body></html>

123
### Example 2: Serving HTML Files (The wrong way / blocking)
Using synchronous methods blocks the server. Do not do this in production!

javascript id="ch10-code-1" const http = require('http'); const fs = require('fs'); const path = require('path');

const server = http.createServer((req, res) => { if (req.url === '/') { // BAD: Blocks the thread while reading const html = fs.readFileSync(path.join(__dirname, 'index.html')); res.writeHead(200, {'Content-Type': 'text/html'}); res.end(html); } });

12
### Example 3: Serving HTML Files (The correct way / non-blocking)

javascript id="ch10-code-2" const http = require('http'); const fs = require('fs'); const path = require('path');

const server = http.createServer((req, res) => { if (req.url === '/') { const filePath = path.join(__dirname, 'index.html'); // GOOD: Reads the file asynchronously fs.readFile(filePath, (err, content) => { if (err) { res.writeHead(500); // 500 means Server Error res.end("Error loading the page"); } else { res.writeHead(200, {'Content-Type': 'text/html'}); res.end(content); } }); } });

1234567891011121314151617181920212223242526272829303132333435363738
---

## 7. Output Explanations

Notice how we combine `path.join(__dirname)` to safely find the HTML file, `fs.readFile` to asynchronously load it, and `res.end(content)` to send the raw Buffer/String over the HTTP response. The browser receives it and renders it perfectly!

---

## 8. Common Mistakes

1. **Forgetting `/` in routes:** The root route is always `'/'`, not `''`. And a path is always `'/about'`, not `'about'`.
2. **Double sending responses:** Writing `res.end()` inside the `if` block, and then accidentally writing `res.end()` again outside the block at the bottom of the function. This will crash the app with an `ERR_STREAM_WRITE_AFTER_END` error. You can only end a response once!
3. **MIME Types:** If you try to serve a CSS file but set the header to `{'Content-Type': 'text/html'}`, the browser will ignore the CSS because it thinks it's looking at HTML. CSS must be `'text/css'`.

---

## 9. Best Practices

- **Switch Statements:** If you have 10 routes, writing 10 `if/else if` statements gets ugly. Use a `switch(req.url)` statement instead.
- **Helper Functions:** Move the `fs.readFile` logic into a reusable helper function so you don't repeat the error handling code for every single route.

---

## 10. Exercises

1. Create a server with a switch statement that handles `/`, `/login`, and `/register`. Return simple text strings for each.
2. Add a `default` case to your switch statement that returns a 404 status code and a "Not Found" message.

---

## 11. Mini Project: Personal website server

**Objective:** Build a server that serves an actual multi-page portfolio site.

**Step 1: Setup HTML files**
Create a `public` folder. Inside it, create `home.html`, `projects.html`, and `404.html`.

**Step 2: Code (`portfolio.js`):**

javascript id="ch10-mini-project" // portfolio.js const http = require('http'); const fs = require('fs'); const path = require('path');

// Helper function to serve files function serveFile(res, fileName, statusCode = 200) { const filePath = path.join(__dirname, 'public', fileName); fs.readFile(filePath, (err, content) => { if (err) { res.writeHead(500, {'Content-Type': 'text/plain'}); res.end("500 - Internal Server Error"); return; } res.writeHead(statusCode, {'Content-Type': 'text/html'}); res.end(content); }); }

const server = http.createServer((req, res) => { console.log(Requested: ${req.url});

switch(req.url) { case '/': case '/home': serveFile(res, 'home.html'); break; case '/projects': serveFile(res, 'projects.html'); break; default: // Send the 404 page and a 404 status code serveFile(res, '404.html', 404); break; } });

server.listen(3000, () => { console.log("Portfolio server running on http://localhost:3000"); }); ``

Run it: node portfolio.js

---

12. Coding Challenges

Challenge 1: Create an API route. If req.url === '/api/user', set the content-type to 'application/json' and res.end() a stringified JSON object containing your name and email.

Challenge 2: Currently, our server can only handle .html files. Research how to dynamically change the Content-Type so you can serve .css and .png files as well.

---

13. MCQs with Answers

Q1: In Node.js routing, which property tells you what path the user is trying to access? A) req.path B) req.url C) res.url D) req.route Answer: B

Q2: Which HTTP status code should you send if a route does not exist? A) 200 B) 400 C) 404 D) 500 Answer: C

Q3: Which module is required to read an HTML file from the hard drive so it can be sent to the client? A) http B) html C) path D) fs Answer: D

Q4: If a user visits http://localhost:3000/, what is the value of req.url? A) localhost B) 3000 C) / D) null Answer: C

---

14. Interview Questions

  1. 1. What is routing in the context of a web server?
*Answer:* Routing is the mechanism by which a server inspects the incoming request's URL and HTTP method, and determines which specific block of code or file should be executed/returned to satisfy that request.
  1. 2. Why is using fs.readFileSync a bad idea in a web server router?
*Answer:* Node.js is single-threaded. If multiple users hit the server at once, a synchronous file read will block the entire event loop, freezing the server for all other users until the file is fully read from the hard drive. Always use the asynchronous fs.readFile.

---

15. FAQs

Q: Writing routes with if/else seems tedious. Is there a better way? A: Yes! As an application grows, doing this manually with the raw http module becomes a nightmare. That is exactly why the Express.js framework was created. We are doing it the hard way now so you understand the magic behind Express later!

Q: Can I serve images using this method? A: Yes, but you have to use fs.readFile without 'utf8' so it reads the image as a binary Buffer, and you must set the header to {'Content-Type': 'image/jpeg'}.

---

16. Summary

  • Routing directs traffic based on the req.url.
  • The root page of a website is always /.
  • Use fs.readFile combined with path.join` to safely read HTML files.
  • Always send a 404 status code for unhandled routes.
  • Helper functions make routing much cleaner and prevent repetitive code.

---

17. Next Chapter Recommendation

We have successfully built a server using only built-in Core modules! But developers don't like reinventing the wheel. There are thousands of pre-built modules on the internet we can download to make our lives easier. In Chapter 11: Understanding NPM, we will learn how to download open-source code to supercharge our applications!

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·