Express.js Mastery / Introduction To Node.js And NPM

Routing

🌐 What Is Routing?

Routing is the logic your server uses to send a different response depending on which URL was requested.

The Problem (Before Routing)

  • 🌍 Currently, no matter what URL is visited (/, /product, /anything), the server always sends back the same response.

  • πŸ”€ We need the server to behave differently depending on the requested path β€” e.g.:

    • πŸ“‹ /overview β†’ show an Overview page

    • πŸ“¦ /product β†’ show a Product page

Note: In large real-world applications, routing gets very complex, so tools like Express are used to handle it (covered later in the course). For now, we're learning to build routing from scratch using plain Node.js.


πŸ“¨ Step 1: Inspecting the Request URL

Every request has a .url property containing the requested path:

console.log(req.url);
  • After restarting the server and reloading the browser, you'll notice two logs appear for a single page visit:

    1. The actual path (e.g., / )

    2. /favicon.ico

πŸ€” Why Two Requests?

  • 🌐 Browsers automatically send a separate request for the site's favicon (the small icon shown in browser tabs).

  • 🚫 Since we don't have a favicon set up, this request can simply be ignored.


🧩 Step 2: Understanding the url Module

  • πŸ“¦ Node has a built-in url module for parsing complex URLs β€” especially ones with query parameters, like:

    /product?id=23&something=else
  • πŸ—‚οΈ The url module can convert these parameters into a clean, structured object.

  • ⏳ Not needed yet for simple routes like /overview or /product β€” it will be used later for more complex URLs.


πŸ”€ Step 3: Implementing Basic Routing with If/Else

Save the URL Path

const pathName = req.url;

Basic Routing Logic

if (pathName === '/overview' || pathName === '/') {
  res.end('This is the OVERVIEW');
} else if (pathName === '/product') {
  res.end('This is the PRODUCT');
} else {
  res.end('<h1>Page not found!</h1>');
}

Explanation

  • 🏠 pathName === '/overview' || pathName === '/' β†’ Both the root URL (/) and /overview show the same Overview page.

  • πŸ“¦ pathName === '/product' β†’ Shows the Product page.

  • 🚫 else β†’ Handles any URL that doesn't match a defined route. A fallback.

⚠️ Without an else fallback, requesting an undefined route (like /something-random) causes the browser to hang indefinitely β€” the loading spinner keeps spinning because the server never sends any response back. Always include a fallback case.


πŸ“„ Step 4: Sending Status Codes and Headers

What Is a Status Code?

  • πŸ”’ An HTTP status code is a number sent back with the response, indicating the outcome of the request.

  • ❌ 404 is the well-known code for "Page Not Found."

  • βœ… 200 means "OK" (a successful response) β€” this is the default when no other code is explicitly set.

What Is a Header?

  • πŸ“ An HTTP header is a piece of metadata about the response (or request) β€” extra context sent alongside the actual content.

Setting Both with writeHead

res.writeHead(404, {
  'Content-Type': 'text/html',
  'my-own-header': 'hello-world'
});
  • πŸ”’ The first argument is the status code (404).

  • πŸ“¦ The second argument is an object containing whichever headers you want to send.

Common Standard Header: Content-Type

'Content-Type': 'text/html'
  • 🌐 This tells the browser to expect HTML in the response body.

  • πŸ–₯️ Once set, you can send actual HTML content:

res.end('<h1>Page not found!</h1>');

The browser will now render this as an <h1> heading instead of displaying it as plain text.

Custom Headers

  • πŸ› οΈ You can also define your own custom headers, for example:

'my-own-header': 'hello-world'

🀫 Verifying Status Codes and Headers

  1. πŸ› οΈ Open Developer Tools:

    • Mac: Cmd + J (or right-click β†’ Inspect)

    • Windows: Alt + Ctrl + J (or View β†’ Developer β†’ Developer Tools)

  2. 🌐 Go to the Network tab.

  3. πŸ”„ Reload the page β€” you'll see the request listed with a 404 status (often shown in red) and, if you click into it, a Response Headers section showing both standard and custom headers.

⚠️ Important Rule

  • ⏳ Status codes and headers must be set before the response body is sent.

  • 🚫 You can't send headers after calling res.end() with content β€” they always need to go through res.writeHead() first.


πŸ§ͺ Testing Everything

  1. πŸ”„ Restart the server (Ctrl + C, then node index.js) after every code change.

  2. 🌐 Visit:

    • 🏠 / or /overview β†’ shows "This is the OVERVIEW"

    • πŸ“¦ /product β†’ shows "This is the PRODUCT"

    • ❌ Any other path (e.g., /random) β†’ shows "Page not found!" with a 404 status and custom headers.

  3. πŸ” Check the Network tab in Dev Tools to confirm:

    • βœ… Status code (200 for success, 404 for not found)

    • πŸ“‹ Response headers (Content-Type, custom headers, etc.)


❌ Important Clarification: Routes β‰  File System

  • Routes like /overview or /product aren't tied to actual folders/files in your project.

  • Visiting /templates won't automatically load a templates folder.

  • To make /templates work, you have to explicitly define it in your routing logic and manually send back a response.


πŸ“š Key Takeaways

πŸ“Œ Concept

🎯 Purpose

req.url

🌐 Gives you the path requested by the client

If/Else on pathName

πŸ”€ Implements basic routing β€” different responses for different URLs

else fallback

πŸ›‘ Prevents the server from hanging on undefined routes

res.writeHead(statusCode, headersObject)

πŸ“¨ Sets the HTTP status code and headers (must be called before res.end())

Content-Type: text/html

πŸ–₯️ Tells the browser to render the response as HTML

404 status code

❌ Standard code for "Page Not Found"

Routes vs File System

πŸ“‚ Routes are custom-defined in code β€” they don't map automatically to folders/files


const fs = require('fs');
const http = require('http');
const url = require('url');
////////////////////////////
//FILES

//Blocking, Synchronous way
//const textIn = fs.readFileSync('./txt/input.txt', 'utf-8');
//console.log(textIn);

//const textOut = `This is what we know about the avocado: ${textIn}.\nCreated on ${Date.now()}`;
//fs.writeFileSync('./txt/output.txt', textOut);

//console.log('File written!');

//Non-blocking, Asynchronous way
// fs.readFile('./txt/start.txt', 'utf8', (err, data1) => {
//   fs.readFile(`./txt/${data1}.txt`, 'utf8', (err, data2) => {
//     console.log(data2);

//     fs.readFile('./txt/append.txt', 'utf8', (err, data3) => {
//       console.log(data2, data3);

//       fs.writeFile('./txt/final.txt', `${data2}\n${data3}`, 'utf8', (err) => {
//         console.log('Your file has been written πŸŽ‰');
//       });
//     });
//   });
// });
/////////////////////////////
//SERVER
const server = http.createServer((req, res) => {
  const pathName = req.url;
 if (pathName === '/overview' || pathName === '/') {
  res.end('This is the OVERVIEW');
} else if (pathName === '/product') {
  res.end('This is the PRODUCT');
} else {
  res.writeHead(404, {
    'Content-Type': 'text/html',
    'my-own-header': 'hello-world'
  });
  res.end('<h1>Page not found!</h1>');
}
});

server.listen(8000, '127.0.0.1', () => {
  console.log('Listening to requests on port 8000');
});