Express: Course Page
Build a course enrollment system with a /courses route that supports both GET
and POST
requests.
The
GET /courses
route should return a list of available courses, but only if the student is logged in.The
POST /courses
route should allow the logged-in student to enroll in a specific course.Upon successful enrollment, create a new cookie named
lastEnrolledCourse
that stores the course name and is valid for 2 minutes.Implement robust error handling for the following scenarios:
Attempting to enroll in a course without being logged in.
Trying to enroll in a course the student has already taken.
app.js
import express from 'express';
import session from 'express-session';
import cookieParser from 'cookie-parser';
// --- App Initialization ---
const app = express();
const PORT = 3001;
// --- Middleware Setup ---
app.use(express.json());
app.use(cookieParser());
app.use(session({
secret: 'a-secure-secret-key',
resave: false,
saveUninitialized: false, // Set to false to avoid empty session objects
cookie: {
maxAge: 2 * 60 * 1000, // 2 minutes
secure: false // In production, set to true for HTTPS
}
}));
// --- In-Memory Data (for demonstration) ---
const courses = [
{ id: 1, name: 'CS Basics' },
{ id: 2, name: 'JavaScript' },
{ id: 3, name: 'Data Structures' }
];
const users = [
{ id: 1, username: 'student1',
password: 'pass1', enrolled: [] },
{ id: 2, username: 'student2',
password: 'pass2', enrolled: [] }
];
// --- API Routes ---
// Login a user and create a session
app.post('/login', (req, res) => {
const { username, password } = req.body;
const user = users.find(u => u.username === username && u.password === password);
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
req.session.userId = user.id;
res.json({ message: 'Login successful' });
});
// Logout a user and destroy the session
app.post('/logout', (req, res) => {
req.session.destroy(err => {
if (err) {
return res.status(500).json({ error: 'Could not log out.' });
}
// The session cookie is cleared automatically
res.json({ message: 'Logged out successfully' });
});
});
// --- Custom Authentication Middleware ---
const auth = (req, res, next) => {
// Find user based on the session ID
const user = users.find(u => u.id === req.session.userId);
if (!user) {
return res.status(401).json({ error: 'Access denied. Please log in.' });
}
// Attach user object to the request for other routes to use
req.user = user;
next();
};
// Get all available and enrolled courses for the logged-in user
app.get('/courses', auth, (req, res) => {
res.json({
availableCourses: courses,
enrolledCourses: req.user.enrolled
});
});
// Enroll the logged-in user in a new course
app.post('/courses', auth, (req, res) => {
const courseId = parseInt(req.body.courseId, 10);
const course = courses.find(c => c.id === courseId);
if (!course) {
return res.status(404).json({ error: 'Course not found' });
}
if (req.user.enrolled.includes(courseId)) {
return res.status(409).json({ error: 'Already enrolled in this course' });
}
req.user.enrolled.push(courseId);
res.cookie('lastEnrolledCourse', course.name, {
maxAge: 2 * 60 * 1000,
httpOnly: true
});
res.status(200).json({ message: `Successfully enrolled in ${course.name}` });
});
// --- Server Start ---
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
FULL Registration and Session with ejs
Updated Express.js
application with the protected course enrollment system.
This implementation adds the GET
and POST
routes for /courses
, includes the required error handling, and sets the lastEnrolledCourse
cookie upon successful enrollment.
app.js
import express from 'express';
import session from 'express-session';
import cookieParser from 'cookie-parser';
import path from 'path';
import { fileURLToPath } from 'url';
const app = express();
const PORT = 3000;
// Needed for __dirname in ES Modules
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// --- Middleware Setup ---
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());
app.use(session({
secret: 'a_secret_key_to_sign_the_cookie',
resave: false,
saveUninitialized: true,
cookie: { secure: false }
}));
// Set EJS as the view engine
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
// --- Data Stores ---
const students = []; // In-memory student database
const availableCourses = ['Calculus 101', 'History of Art', 'Introduction to Python', 'Quantum Physics'];
// Custom middleware to check for authenticated session
const authMiddleware = (req, res, next) => {
if (req.session && req.session.student) {
return next();
}
res.status(403).send('Access denied: Please login first.');
};
// --- Routes ---
// Registration and Login Routes
app.get('/register', (req, res) => res.render('register'));
app.post('/register', (req, res) => {
const { rollNo, name, password } = req.body;
// Add enrolledCourses array for each new student
students.push({ rollNo, name, password, enrolledCourses: [] });
console.log('Students:', students);
res.redirect('/login');
});
app.get('/login', (req, res) => res.render('login'));
app.post('/login', (req, res) => {
const { rollNo, password } = req.body;
const student = students.find(s => s.rollNo === rollNo && s.password === password);
if (student) {
req.session.student = { rollNo: student.rollNo, name: student.name };
res.cookie('studentPortalAccess', student.rollNo, { maxAge: 3 * 60 * 1000 });
res.redirect('/dashboard');
} else {
res.status(401).send('Invalid roll number or password.');
}
});
// Protected Dashboard and Result Routes
app.get('/dashboard', authMiddleware, (req, res) => {
res.render('dashboard', { student: req.session.student });
});
app.get('/result', authMiddleware, (req, res) => {
res.status(200).send(`Hi ${req.session.student.name}, your results are available!`);
});
// NEW: Course Enrollment Routes
app.get('/courses', authMiddleware, (req, res) => {
const studentRecord = students.find(s => s.rollNo === req.session.student.rollNo);
res.render('courses', {
courses: availableCourses,
enrolled: studentRecord.enrolledCourses
});
});
app.post('/courses', authMiddleware, (req, res) => {
const { course } = req.body;
const studentRecord = students.find(s => s.rollNo === req.session.student.rollNo);
// Error handling: Check if course exists or if student is already enrolled
if (!availableCourses.includes(course)) {
return res.status(400).send('Error: This course does not exist.');
}
if (studentRecord.enrolledCourses.includes(course)) {
return res.status(400).send('Error: You are already enrolled in this course.');
}
// Enroll the student
studentRecord.enrolledCourses.push(course);
// Set the new cookie for 2 minutes
res.cookie('lastEnrolledCourse', course, { maxAge: 2 * 60 * 1000 });
console.log(`${studentRecord.name} enrolled in ${course}.`);
res.redirect('/courses');
});
// Logout Route
app.get('/logout', (req, res) => {
req.session.destroy(err => {
if (err) return res.redirect('/dashboard');
res.clearCookie('studentPortalAccess');
res.clearCookie('lastEnrolledCourse'); // Also clear the enrollment cookie
res.redirect('/login');
});
});
// Start the server
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}/register`);
});
EJS Files
You will need to add a new courses.ejs
file and update dashboard.ejs
with a link to it.
views/courses.ejs
(New File)
<!DOCTYPE html>
<html lang="en">
<head><title>Course Enrollment</title></head>
<body>
<h1>Course Enrollment</h1>
<h2>Your Enrolled Courses:</h2>
<% if (enrolled.length > 0) { %>
<ul>
<% enrolled.forEach(course => { %>
<li><%= course %></li>
<% }) %>
</ul>
<% } else { %>
<p>You are not enrolled in any courses yet.</p>
<% } %>
<hr>
<h2>Enroll in a New Course:</h2>
<form action="/courses" method="POST">
<label for="course">Select a course:</label>
<select name="course" id="course">
<% courses.forEach(course => { %>
<option value="<%= course %>"><%= course %></option>
<% }) %>
</select>
<button type="submit">Enroll</button>
</form>
<p><a href="/dashboard">Back to Dashboard</a></p>
</body>
</html>
views/dashboard.ejs
(Updated)
<!DOCTYPE html>
<html lang="en">
<head><title>Dashboard</title></head>
<body>
<h1>Welcome to the Student Portal, <%= student.name %>!</h1>
<p>Your Roll Number is: <%= student.rollNo %></p>
<p><a href="/result">View Your Results</a></p>
<p><a href="/courses">Course Enrollment</a></p> <a href="/logout">Logout</a>
</body>
</html>
Run and Test
npm init -y
npm install express express-session cookie-parser ejs
Then, ensure your package.json
includes "type": "module"
.
node app.js
Log In: Go to
http://localhost:3000/register
to create an account, then log in.Access Courses Page: From the dashboard, click the "Course Enrollment" link.
Enroll: Select a course from the dropdown menu (e.g., "Introduction to Python") and click "Enroll".
Verify Enrollment: The page will reload, and "Introduction to Python" will now appear under "Your Enrolled Courses."
Check Cookie: Open your browser's developer tools and look at the cookies for the site. A new cookie named
lastEnrolledCourse
will be present with the value "Introduction to Python" and an expiry time of 2 minutes.Test Error Handling: Try to enroll in "Introduction to Python" again. The server will respond with a
400 Bad Request
status and the message: "Error: You are already enrolled in this course."Test Unauthorized Access: Log out, then try to access
http://localhost:3000/courses
directly. You will see the "Access denied" message.