Introduction to Routing
In this assignment, you will build a simple 3-page website using Express. The goal is to introduce routing concepts and practice serving static files like HTML and CSS. You will create and serve static HTML files for the Home, About, and Products routes, set up and serve a static CSS file to style your website, and learn the basics of routing in Express using res.sendFile().
In future assignments, we will explore more advanced routing techniques and transition to using a templating engine like EJS. Templating engines simplify managing dynamic views by allowing us to reuse components and pass data into our HTML files. For now, we will focus on understanding the fundamentals of routing and file organization.
Project Structure Overview
Before we begin, let's understand how we'll organize our project and why this organization matters. We'll be working with two main directories that serve fundamentally different purposes:
-
public/: Contains static files that browsers can access directly (CSS, images, client-side JavaScript) -
src/views/: Contains HTML files that Express serves through routes
This separation reflects how modern web applications work. Files in public/ are served exactly as they exist on your server – no processing, no security checks, no logic. They're perfect for assets that never change: stylesheets, images, fonts, and client-side JavaScript files. Anyone can request these directly by typing the URL.
Files in src/views/, on the other hand, are served through your application logic. Express decides whether to serve them, can modify them before serving, can apply security checks, and can even generate them dynamically. This gives you complete control over what users see and when they see it.
By the end of this assignment, your project structure will look like this:
[project-root]
├── public/
│ └── css/
│ └── main.css
├── src/
│ └── views/
│ ├── about.html
│ ├── home.html
│ └── products.html
├── .env
└── server.js
Preparation
We will use the Express app you previously set up in your practice repository. Follow these steps to prepare your project:
1. Update Environment Configuration
Modify your .env file by deleting its content and adding only the following line:
PORT=3000
This defines the port your server will run on. Environment variables like this allow you to manage configuration separately from your code, making it easier to deploy your application in different environments. Update your server.js file to conditionally use this port number:
const PORT = process.env.PORT || 3000;
If your .env file is ever missing or you forgot to set a port number, this will allow the server to start by defaulting the port value to 3000. Depending on the service you will use to host your website in the future, you will need to change this hardcoded value to the number supported by that service.
When using conditional (default) values in your code, exercise caution! While setting defaults helps your application handle missing configuration and fail gracefully, avoid using them in ways that could introduce security risks or unintended behavior. Defaults should only be used when it's safe for your application to recover from errors. For more on this topic, see graceful error handling.
2. Update Your Structure
So far we have not put a lot of thought into how we structure our code. Now that we are starting to add more files and declarations, it's important to organize our project in a way that makes it easy to navigate and maintain. Please carefully reorganize your project structure to match the following outline:
// Imports
...
/**
* Declare Important Variables
*/
const PORT = process.env.PORT || 3000;
/**
* Setup Express Server
*/
const app = express();
/**
* Declare Routes
*/
...
// Start the server and listen on the specified port
...
Throughout this course you should periodically review your code structure and organization. As you add more files, functions, and features, keeping your code organized will help you and others understand it better. This is especially important in larger projects where multiple developers may be working together.
3. Create Directory Structure and CSS File
Create the following directories and files:
-
Create the
public/cssdirectories at the root of your project -
Create a file named
main.cssinside thecssdirectory with the following content:html, body { background-color: lightblue; font-family: Arial, sans-serif; margin: 0; padding: 20px; } body { nav { ul { list-style-type: none; padding: 0; } li { display: inline; margin-right: 15px; } a { text-decoration: none; color: #333; font-weight: bold; &:hover { color: #0066cc; } } } } -
Create the
src/viewsdirectories (we'll add HTML files here in the next section)
When we ask you to create files or directories in this course, make sure to create them in the correct location. The public directory should be at the root of your project, at the same level as your server.js file. The src directory should also be at the root level, separate from public.
Assignment Instructions
1. Serve Static Files
Static files are assets that do not change when served to users, such as CSS files, images, and client-side JavaScript. Express provides a simple way to serve these files using the express.static middleware. This built-in function lets you serve your static files directly to the browser without any processing.
Before we can serve files, we need to understand how JavaScript handles file paths. In traditional CommonJS modules (using require()), Node.js automatically provided two special variables: __dirname and __filename. These variables solved a critical problem in server applications: how do you reliably find files when you don't know where your application will be running?
__dirname contains the absolute path to the directory containing the current JavaScript file. __filename contains the absolute path to the current JavaScript file itself. These are essential because they provide consistent references regardless of where someone runs your application from. Without them, if someone ran your server from a different directory, all your file paths would break.
However, ES modules (which we're using with import statements) don't automatically provide these variables. Instead, we must create them ourselves using the newer ES module system. Add these imports to the top of your server.js file:
import { fileURLToPath } from 'url';
import path from 'path';
Then, in your important variables section, recreate the __dirname and __filename variables:
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
Here's what's happening: import.meta.url gives us the URL of the current module, fileURLToPath() converts that URL to a file system path, and path.dirname() extracts just the directory portion. This recreates the same functionality that CommonJS provided automatically.
Now, add the following middleware to your server.js file after const app = express(); but before any routes:
/**
* Configure Express middleware
*/
// Serve static files from the public directory
app.use(express.static(path.join(__dirname, 'public')));
This code tells Express that any file in your public directory should be accessible directly through your website. For example, your CSS file at public/css/main.css will be available at http://127.0.0.1:3000/css/main.css. Notice that the public part is not included in the URL.
The express.static middleware automatically maps the contents of your public directory to the root of your website. This keeps your URLs clean and makes it easy to organize static assets. When someone requests /css/main.css, Express looks for public/css/main.css and serves it if found.
2. Create Static HTML Files
Now create three HTML files in your src/views directory. These files will serve as the content for your different pages:
home.html– Your main landing pageabout.html– A page to introduce yourselfproducts.html– A page to showcase your products
Use this template for each file, replacing the placeholders with appropriate content for each page:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>[PAGE TITLE]</title>
<link rel="stylesheet" href="/css/main.css">
</head>
<body>
<h1>[PAGE HEADING]</h1>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/products">Products</a></li>
</ul>
</nav>
<p>[Add some content specific to this page]</p>
</body>
</html>
Notice how the CSS file is linked using a root-relative path (/css/main.css). This works because Express serves everything in your public directory from the root URL of your website.
3. Set Up Routes
Now let's create routes so people can actually visit your pages. A route is simply a rule that says "when someone visits this URL, send them this content." It's like a receptionist who knows which office to direct visitors to.
Replace the entire "Declare Routes" section in server.js, including any routes you may have added, with the following code:
/**
* Routes
*/
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'src/views/home.html'));
});
app.get('/about', (req, res) => {
res.sendFile(path.join(__dirname, 'src/views/about.html'));
});
app.get('/products', (req, res) => {
res.sendFile(path.join(__dirname, 'src/views/products.html'));
});
Here is a breakdown of what each part does:
-
app.get('/', ...)tells Express "when someone visits the homepage (just the domain with no path), do something" -
(req, res) => { ... }is the function that runs when someone visits that URL.reqcontains information about what they requested,reslets you send something back -
res.sendFile(...)sends an HTML file to their browser -
path.join(__dirname, 'src/views/home.html')creates the complete path to your HTML file
So when someone types http://127.0.0.1:3000/ in their browser, Express finds the first route, runs the function, and sends them your home.html file.
The same thing happens for the other routes:
-
http://127.0.0.1:3000/about→ sendsabout.html -
http://127.0.0.1:3000/products→ sendsproducts.html
You might wonder why we don't just put our HTML files in the public directory like our CSS. The reason is control. Routes let you do things before sending the file: check if the user is logged in, customize the content, or redirect them somewhere else. Static files in public are sent directly with no opportunity for your code to intervene.
One Important Detail: We use path.join(__dirname, ...) instead of just writing 'src/views/home.html' because we need the complete, absolute path to the file. __dirname gives us the directory where server.js lives, and path.join() properly combines it with the rest of the path. This ensures the file is found no matter where someone runs your application from.
4. Build A Real About Me
Now that you have your routes set up, it's time to make your about.html page more personal. Replace any placeholder content with information about yourself. At a minimum your page should include:
- Your name
- A brief introduction about yourself
- A list of your hobbies or interests
-
A picture of yourself saved in the
public/imagesdirectory
Don't forget to update your main.css with styles that make your about page visually appealing. You can add colors, fonts, and layout styles to enhance the user experience. All CSS should follow modern CSS nesting conventions, which allow you to write cleaner and more organized styles.
5. Test Your Website
Start your server using pnpm run dev and test each page in your browser:
- http://127.0.0.1:3000/ should display your home page
- http://127.0.0.1:3000/about should display your about page
- http://127.0.0.1:3000/products should display your products page
Verify that each page shows its correct content, the navigation works, and the CSS styles are applied. If you encounter issues, check your file paths and routes for typos. The browser's developer tools can also help identify problems with missing files or broken links.
Check Your Understanding
A student just learned the basics of Express.js routing by serving static files. They're working on two coding challenges using this template:\n app.get(..., (req, res) => { ... });\n Review their code and give direct feedback to the student. Tell them what they did well and where they need to improve. The relative URL task is a trick question - both "/" and "./" work, but "../" doesn't. If they used string concatenation correct them appropriately, template literals should be used instead and "path.join" should be used for paths. Avoid teaching new concepts or writing code for them. Instead, guide them to think through their mistakes and suggest they try again if needed. If they continue to struggle, act as a tutor and help them understand the concepts better.
/**
* Create an Express route that serves a static HTML file and meets the following requirements:
* 1. The route should be accessible at the URL path '/test-1'.
* 2. The HTML file should be named 'test-1.html' and located in the 'src/views' directory.
* 3. You should serve the file using an absolute path, not a relative path.
*/
app.get(..., (req, res) => {
...
});
/**
* Create an Express route that serves a static HTML file and meets the following requirements:
* 1. The route should be accessible at the URL path '/test-2'.
* 2. The HTML file should be named 'test-2.html' and located in the 'src/views' directory.
* 3. You should serve the file using a relative path, not a absolute path.
*/
app.get(..., (req, res) => {
...
});
Key Concepts Summary
This assignment introduced several fundamental concepts:
- Static Files: Assets like CSS and images that are served directly to users without processing
- Middleware: Functions that process requests before they reach your routes
- Routing: Mapping URLs to specific responses or content
-
File Organization: Separating static assets (
public/) from server-rendered content (src/views/) -
Path Handling: Using
path.join()to create reliable file paths across different operating systems
Understanding these concepts is crucial as they form the foundation for more advanced Express features. In the next assignment, you'll learn about templating engines, which allow you to create dynamic content and reusable components.
Experiment and Extend
Now that you have a functional multi-page website, try these optional enhancements:
- Add more pages and corresponding routes
- Enhance your CSS with more styling, colors, or layout improvements
- Experiment with different HTML content and structure
The more you practice with these fundamentals, the more comfortable you'll become with Express and web development concepts.