Building a Course Catalog with Route Parameters
In this assignment, you will create a simple course catalog system that demonstrates how route parameters work in Express. You will build two connected pages: a catalog list that shows available courses and individual course detail pages that display specific information based on the course ID captured from the URL.
This hands-on practice will help you understand how route parameters capture dynamic values from URLs and use them to display different content, building directly on the concepts you learned in the reading activity.
Setting Up Your Course Data
Since we have not yet covered databases or external data sources, you will work with a hardcoded JavaScript object that represents course information. Copy and paste this course data structure into your server.js file, placing it near the top after your imports but before your routes:
// Course data - place this after imports, before routes
const courses = {
'CS121': {
id: 'CS121',
title: 'Introduction to Programming',
description: 'Learn programming fundamentals using JavaScript and basic web development concepts.',
credits: 3,
sections: [
{ time: '9:00 AM', room: 'STC 392', professor: 'Brother Jack' },
{ time: '2:00 PM', room: 'STC 394', professor: 'Sister Enkey' },
{ time: '11:00 AM', room: 'STC 390', professor: 'Brother Keers' }
]
},
'MATH110': {
id: 'MATH110',
title: 'College Algebra',
description: 'Fundamental algebraic concepts including functions, graphing, and problem solving.',
credits: 4,
sections: [
{ time: '8:00 AM', room: 'MC 301', professor: 'Sister Anderson' },
{ time: '1:00 PM', room: 'MC 305', professor: 'Brother Miller' },
{ time: '3:00 PM', room: 'MC 307', professor: 'Brother Thompson' }
]
},
'ENG101': {
id: 'ENG101',
title: 'Academic Writing',
description: 'Develop writing skills for academic and professional communication.',
credits: 3,
sections: [
{ time: '10:00 AM', room: 'GEB 201', professor: 'Sister Anderson' },
{ time: '12:00 PM', room: 'GEB 205', professor: 'Brother Davis' },
{ time: '4:00 PM', room: 'GEB 203', professor: 'Sister Enkey' }
]
}
};
This object structure allows you to look up course information using bracket notation like courses['CS121'], which is exactly what you will do when users visit URLs like /catalog/CS121.
Assignment Instructions
1. Create the Course Catalog List Page
Start by creating a route that displays all available courses in a simple list. Add this route to your server.js file before your error handling middleware:
// Course catalog list page
app.get('/catalog', (req, res) => {
res.render('catalog', {
title: 'Course Catalog',
courses: courses
});
});
Create the corresponding template catalog.ejs in your views directory:
<%- include('partials/header') %>
<main>
<h1>Course Catalog</h1>
<p>Browse our available courses and click on any course to see detailed information including available sections and instructors.</p>
<div class="course-list">
<% Object.values(courses).forEach(course => { %>
<div class="course-card">
<h3><a href="/catalog/<%= course.id %>"><%= course.title %></a></h3>
<p class="course-id"><%= course.id %> • <%= course.credits %> credits</p>
<p class="course-description"><%= course.description %></p>
</div>
<% }); %>
</div>
</main>
<%- include('partials/footer') %>
Add a link to the catalog in your navigation. Update your header.ejs partial:
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/catalog">Course Catalog</a></li>
<li><a href="/products">Products</a></li>
</ul>
</nav>
Test your catalog page by visiting http://127.0.0.1:3000/catalog. You should see a list of three courses, each with a clickable link.
2. Create the Course Detail Route with Parameters
Now implement the route that captures a course ID from the URL and displays detailed information about that specific course. Add this route after your catalog route:
// Course detail page with route parameter
app.get('/catalog/:courseId', (req, res) => {
// Extract the course ID from the URL
const courseId = req.params.courseId;
// Look up the course in our data
const course = courses[courseId];
// Handle course not found
if (!course) {
const err = new Error(`Course ${courseId} not found`);
err.status = 404;
return next(err);
}
// Log the parameter for debugging
console.log('Viewing course:', courseId);
// Render the course detail template
res.render('course-detail', {
title: `${course.id} - ${course.title}`,
course: course
});
});
Create the course detail template course-detail.ejs in your views directory:
<%- include('partials/header') %>
<main>
<div class="course-header">
<h1><%= course.title %></h1>
<p class="course-meta"><%= course.id %> • <%= course.credits %> credits</p>
<p class="course-description"><%= course.description %></p>
</div>
<div class="sections">
<h2>Available Sections</h2>
<div class="section-list">
<% course.sections.forEach(section => { %>
<div class="section-card">
<div class="section-time"><%= section.time %></div>
<div class="section-room">Room: <%= section.room %></div>
<div class="section-professor">Instructor: <%= section.professor %></div>
</div>
<% }); %>
</div>
</div>
<div class="navigation">
<p><a href="/catalog">← Back to Course Catalog</a></p>
</div>
</main>
<%- include('partials/footer') %>
Test your route parameter by clicking on course links from the catalog page. Try visiting URLs directly like /catalog/CS121, /catalog/MATH110, and /catalog/ENG101. Check your server console to see the logged course ID.
The :courseId in your route pattern tells Express to capture whatever comes after /catalog/ and make it available as req.params.courseId. When someone visits /catalog/CS121, Express captures "CS121" and lets you use it to look up the specific course data.
3. Add Query Parameter Sorting
Enhance your course detail page by adding the ability to sort sections using query parameters. Replace your course detail route with the following code:
// Enhanced course detail route with sorting
app.get('/catalog/:courseId', (req, res, next) => {
const courseId = req.params.courseId;
const course = courses[courseId];
if (!course) {
const err = new Error(`Course ${courseId} not found`);
err.status = 404;
return next(err);
}
// Get sort parameter (default to 'time')
const sortBy = req.query.sort || 'time';
// Create a copy of sections to sort
let sortedSections = [...course.sections];
// Sort based on the parameter
switch (sortBy) {
case 'professor':
sortedSections.sort((a, b) => a.professor.localeCompare(b.professor));
break;
case 'room':
sortedSections.sort((a, b) => a.room.localeCompare(b.room));
break;
case 'time':
default:
// Keep original time order as default
break;
}
console.log(`Viewing course: ${courseId}, sorted by: ${sortBy}`);
res.render('course-detail', {
title: `${course.id} - ${course.title}`,
course: { ...course, sections: sortedSections },
currentSort: sortBy
});
});
And replace your course-detail.ejs template with this updated version that includes sorting links:
<%- include('partials/header') %>
<main>
<div class="course-header">
<h1><%= course.title %></h1>
<p class="course-meta"><%= course.id %> • <%= course.credits %> credits</p>
<p class="course-description"><%= course.description %></p>
</div>
<div class="sections">
<div class="sections-header">
<h2>Available Sections</h2>
<div class="sort-options">
<span>Sort by: </span>
<a href="/catalog/<%= course.id %>" class="<%= currentSort === 'time' ? 'active' : '' %>">Time</a>
<a href="/catalog/<%= course.id %>?sort=professor" class="<%= currentSort === 'professor' ? 'active' : '' %>">Professor</a>
<a href="/catalog/<%= course.id %>?sort=room" class="<%= currentSort === 'room' ? 'active' : '' %>">Room</a>
</div>
</div>
<div class="section-list">
<% course.sections.forEach(section => { %>
<div class="section-card">
<div class="section-time"><%= section.time %></div>
<div class="section-room">Room: <%= section.room %></div>
<div class="section-professor">Instructor: <%= section.professor %></div>
</div>
<% }); %>
</div>
</div>
<div class="navigation">
<p><a href="/catalog">← Back to Course Catalog</a></p>
</div>
</main>
<%- include('partials/footer') %>
Test the sorting functionality by clicking the sort links on any course detail page. Try URLs like:
/catalog/CS121?sort=professor/catalog/MATH110?sort=room/catalog/ENG101?sort=time
4. Add Styling with Native CSS Nesting
Create a new CSS file catalog.css in your public/css/ directory and add these modern CSS styles using native nesting:
/* Course catalog styles using native CSS nesting */
.course-list {
display: grid;
gap: 1.5rem;
max-width: 800px;
margin: 0 auto;
.course-card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 1.5rem;
background: #fff;
transition: box-shadow 0.2s ease;
&:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
h3 {
margin: 0 0 0.5rem 0;
a {
color: #2c5aa0;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
.course-id {
color: #666;
font-weight: 500;
margin: 0 0 1rem 0;
}
.course-description {
color: #444;
line-height: 1.5;
margin: 0;
}
}
}
.course-header {
max-width: 800px;
margin: 0 auto 2rem auto;
text-align: center;
h1 {
color: #2c5aa0;
margin-bottom: 0.5rem;
}
.course-meta {
color: #666;
font-weight: 500;
margin: 0 0 1rem 0;
}
.course-description {
color: #444;
line-height: 1.6;
font-size: 1.1rem;
}
}
.sections {
max-width: 800px;
margin: 0 auto;
.sections-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
border-bottom: 2px solid #2c5aa0;
padding-bottom: 0.5rem;
h2 {
margin: 0;
color: #2c5aa0;
}
.sort-options {
display: flex;
gap: 1rem;
align-items: center;
span {
color: #666;
font-weight: 500;
}
a {
color: #2c5aa0;
text-decoration: none;
padding: 0.25rem 0.5rem;
border-radius: 4px;
&:hover {
background: #f0f4f8;
}
&.active {
background: #2c5aa0;
color: white;
}
}
}
}
.section-list {
display: grid;
gap: 1rem;
.section-card {
border: 1px solid #ddd;
border-radius: 6px;
padding: 1rem;
background: #f9f9f9;
.section-time {
font-weight: bold;
color: #2c5aa0;
font-size: 1.1rem;
margin-bottom: 0.5rem;
}
.section-room,
.section-professor {
color: #555;
margin-bottom: 0.25rem;
}
}
}
}
.navigation {
max-width: 800px;
margin: 2rem auto 0 auto;
text-align: center;
a {
color: #2c5aa0;
text-decoration: none;
font-weight: 500;
&:hover {
text-decoration: underline;
}
}
}
Link your new CSS file in your header.ejs partial by adding this line in the head section:
<link rel="stylesheet" href="/css/catalog.css">
Refresh your catalog pages to see the improved styling with modern CSS nesting in action.
The CSS you just added uses native browser support for nesting selectors, eliminating the need for preprocessors like Sass or Less. This modern approach keeps your styles organized and maintainable while working directly in standard CSS.
5. Test Invalid Course IDs
Your route already includes error handling for invalid course IDs. Test this by visiting a non-existent course like /catalog/INVALID123. You should see your custom 404 error page, demonstrating how route parameter validation connects with your existing error handling system.
Try these invalid URLs to confirm your error handling works properly:
/catalog/FAKE101/catalog/12345/catalog/not-a-course
Key Concepts Summary
You have successfully built a functional course catalog system that demonstrates the fundamental concepts of route parameters in Express. Your application captures course IDs from the URL structure, uses them to look up and display specific course information, and handles invalid requests gracefully through your existing error system.
The sorting functionality shows how route parameters and query parameters work together: the route parameter identifies which course to display, while the query parameter modifies how that course's sections are presented. This combination creates flexible, user-friendly URLs that maintain their meaning while providing customization options.
These concepts form the foundation for building dynamic web applications that respond to user navigation while maintaining clean, understandable URL structures.
Experiment and Extend
Practice the route parameter concepts you just learned with these enhancements:
- Add More Courses: Expand the courses object with additional classes like "HIST101" or "BIOL150" and test your routes with the new data.
-
Create a Random Course Feature: Add a route like
/catalog/randomthat redirects to a randomly selected course detail page. - Enhanced Validation: Add validation to ensure course IDs follow the expected pattern (letters followed by numbers) before looking them up.