0% found this document useful (0 votes)
52 views31 pages

Report

This document provides a comprehensive analysis of modern web programming technologies, focusing on the differences between relational databases (RDBMS) and NoSQL databases, as well as comparing the XAMPP and MEVN development stacks. It discusses the advantages and disadvantages of each technology, highlighting their applications in web development, and emphasizes the importance of choosing the right database and technology stack for project requirements. The report also identifies areas for improvement in user experience, performance, and technical debt in a demo web system.

Uploaded by

sonbim1999
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
52 views31 pages

Report

This document provides a comprehensive analysis of modern web programming technologies, focusing on the differences between relational databases (RDBMS) and NoSQL databases, as well as comparing the XAMPP and MEVN development stacks. It discusses the advantages and disadvantages of each technology, highlighting their applications in web development, and emphasizes the importance of choosing the right database and technology stack for project requirements. The report also identifies areas for improvement in user experience, performance, and technical debt in a demo web system.

Uploaded by

sonbim1999
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 31

Web Programming Technologies: A Comprehensive Analysis of Modern

Database and Development Approaches

I. Introduction

The digital landscape of web development is undergoing a profound


transformation, driven by emerging technologies that challenge traditional
paradigms of software architecture and data management. This report embarks on a
comprehensive exploration of modern web programming technologies, focusing on
the intricate dynamics between relational and non-relational database systems and
comparing traditional and contemporary web development stacks.

The rapid evolution of web technologies demands a critical understanding of how


different approaches to database management and application development can
significantly impact performance, scalability, and overall system design. By
examining the RDBMS and NoSQL database models and comparing the XAMPP
and MEVN technological stacks, we aim to provide insights into the complex
ecosystem of modern web development.

II. RDBMS & NoSQL Databases: A Deep Dive

Databases are the backbone of modern applications, storing and managing vast
amounts of data. Choosing the right type of database is critical to meeting project
requirements. RDBMS, characterized by structured tables and relationships, excels
in handling predictable, structured data with complex interconnections. In contrast,
NoSQL databases offer flexibility, scalability, and high performance, catering to
dynamic, unstructured, or large-scale data needs. Understanding their differences,
strengths, and limitations can help businesses make informed decisions for
operational and analytical tasks.

1. RDBMS, short for Relational Database Management System, is a traditional


database system that organizes data into tables with rows and columns. It
stores data following a structured schema and uses SQL for querying and
manipulation.
Th
is image shows an answers table in phpMyAdmin, demonstrating key RDBMS
features like a primary key (answer_id) for unique identification, foreign keys
(user_id, question_id) for relationships with other tables, and constraints (NOT
NULL, AUTO_INCREMENT) to enforce data integrity. The created_at column
automatically timestamps entries, reflecting RDBMS's structured approach for
managing relational data efficiently.

NoSQL databases, such as MongoDB, are non-relational database systems


designed to handle unstructured, semi-structured, or structured data. Unlike
RDBMS, NoSQL databases use flexible schema designs, storing data in formats
like key-value pairs, documents, wide columns, or graphs.
The provided image displays a MongoDB database in Atlas, where collections are
organized within a database (sonnbgch221038). Each document is identified by a
unique _id (e.g., ObjectId('5d34c59c098c00453a233bea')). Documents contain
key-value pairs, such as fsa_id: 523718 and name: "1 Town Hall Square". Nested
fields, like address and location, illustrate the hierarchical nature of NoSQL,
enabling complex, dynamic data structures without predefined schemas. This
flexibility is ideal for use cases requiring scalability and diverse data modeling.

Comparison of XAMPP and MEVN Stack


1. XAMPP Overview

XAMPP is an open-source cross-platform web server solution designed to make


local web development easier. It includes components like:

 Apache: A powerful and flexible HTTP server.


 MySQL: A relational database management system for storing application
data.
 PHP: A server-side scripting language used for building dynamic web
applications.
 Perl: For additional server-side scripting needs.

Analysis & Evaluation:

 Advantages:
o Easy setup for beginners.
o Bundles all necessary components for a complete web server.
o Local testing and debugging are straightforward with tools like
phpMyAdmin.
o Suitable for small to medium-sized projects.
 Disadvantages:
o Scalability is limited for enterprise-level applications.
o Technology stack is not optimized for real-time applications.
o Requires manual configuration for advanced features.

Application in Projects: In your report, XAMPP was used for a Q&A platform. It
offered:

 A structured development environment with PHP for backend logic and


MySQL for database operations.
 A reliable solution for rapid prototyping during the development phase
(NGUYEN_BAO_SON_00132465…).

2. MEVN Stack Overview

MEVN is a modern JavaScript-based stack for building full-stack applications:

 MongoDB: NoSQL database for storing JSON-like data.


 Express.js: Backend web application framework for Node.js.
 Vue.js: Frontend framework for building user interfaces.
 Node.js: JavaScript runtime environment for server-side scripting.

Analysis & Evaluation:

 Advantages:
o Full JavaScript stack allows seamless development across frontend
and backend.
o MongoDB provides flexibility for handling unstructured and evolving
datasets.
o Vue.js offers a reactive and component-based framework for dynamic
UI development.
o Suitable for scalable, real-time, and API-driven applications.
 Disadvantages:
o Steeper learning curve compared to XAMPP.
o Requires separate deployment of frontend and backend applications.
o Debugging a NoSQL database like MongoDB may be challenging for
those accustomed to relational databases.

Application Scenarios: MEVN excels in building modern, scalable, and


interactive web applications such as single-page applications (SPAs) or real-time
apps like chat systems or dashboards.

3. Key Differences Between XAMPP and MEVN


Aspect XAMPP MEVN Stack
Architecture LAMP/WAMP-based Full JavaScript stack
Database MySQL (Relational) MongoDB (NoSQL)
Frontend Traditional server-rendered HTML Vue.js for

Technology Discussion

Backend Technology: Node.js and Express.js

Concept:

 Node.js is a JavaScript runtime environment built on Chrome's V8 engine. It


enables server-side scripting, allowing JavaScript to be used for backend
development.
 Express.js is a lightweight web application framework for Node.js. It
simplifies building web servers and APIs by offering features like routing,
middleware, and request/response handling.

Characteristics:

 Node.js: Event-driven, non-blocking I/O, scalable, and highly efficient for


real-time applications.
 Express.js: Minimalistic, extensible via middleware, and supports RESTful
APIs.

Advantages:

 Node.js:
o High scalability with asynchronous processing.
o Uses JavaScript for both frontend and backend, simplifying
development.
o Rich package ecosystem via npm.
 Express.js:
o Simplifies routing and middleware configuration.
o Wide community support and documentation.

Disadvantages:

 Node.js:
o Single-threaded, which can be a bottleneck for CPU-intensive tasks.
o Callback-heavy code can lead to issues like "callback hell."
 Express.js:
o Limited built-in features; requires many external libraries.

Code Example (Express.js API):

javascript

Copy code

const express = require('express'); const app = express(); app.get('/', (req, res) =>
{ res.send('Hello, world!'); }); app.listen(3000, () => { console.log('Server running
on port 3000'); });
Frontend Technology: Vue.js

Concept:

 Vue.js is a progressive JavaScript framework for building user interfaces. It


focuses on declarative rendering and component-based architecture.

Characteristics:

 Virtual DOM for faster updates.


 Two-way data binding via the v-model directive.
 Modular components for scalable applications.

Advantages:

 Easy to learn with a simple syntax.


 Reactive data binding and state management.
 Lightweight and high performance.

Disadvantages:

 Smaller community compared to React or Angular.


 Dependency on third-party libraries for advanced features.

Code Example:

javascript

Copy code

<template> <div> <h1>{{ message }}</h1> </div> </template> <script> export


default { data() { return { message: "Hello Vue.js!", }; }, }; </script>

Database Technology: MongoDB

Concept:

 MongoDB is a NoSQL database that stores data in flexible, JSON-like


documents.
Characteristics:

 Schema-less design, enabling dynamic data structures.


 Scales horizontally through sharding.
 Supports ACID transactions since version 4.0.

Advantages:

 Flexible and scalable for big data.


 Fast read/write speeds for unstructured data.
 Rich querying support with aggregation pipelines.

Disadvantages:

 Not suitable for complex relationships compared to SQL databases.


 Can lead to data duplication due to lack of normalization.

Code Example (Connecting to MongoDB using Mongoose):

javascript

Copy code

const mongoose = require('mongoose');


mongoose.connect('mongodb://localhost:27017/mydb', { useNewUrlParser: true,
useUnifiedTopology: true }); const userSchema = new mongoose.Schema({ name:
String, age: Number }); const User = mongoose.model('User', userSchema); const
newUser = new User({ name: 'John', age: 30 }); newUser.save().then(() =>
console.log('User saved'));

Popular Libraries

 Mongoose: Simplifies MongoDB operations and provides schema


validation.
 Body-parser: Parses incoming request bodies in middleware, particularly
for JSON payloads.
 Axios: HTTP client for making API calls, used on both client and server
sides.
 Cors: Enables Cross-Origin Resource Sharing (CORS) for secure API
access from other origins.
Used Tools

 Visual Studio Code:


o Lightweight code editor with support for extensions like Prettier,
ESLint, and Git integration.
o Ideal for MEVN stack projects due to JavaScript-centric tools and
debugging features.

Cloud Technology

 MongoDB Atlas: Managed cloud service for MongoDB. Features include


backup, scalability, and automated updates.
 Render: Platform for deploying full-stack web applications, including
Node.js backends.
 Vercel: Focuses on frontend hosting, providing optimized deployment
pipelines for Vue.js or other SPAs.

Pros of Demo Web System:

 User Authentication & Security


 Robust login system with proper error handling
 Secure password management
 Remember me functionality for better user experience

async login() {

try {

await this.$store.dispatch("login", {

email: this.email,

password: this.password,

rememberMe: this.rememberMe

});
if (this.isUserLoggedIn) {

await this.$router.push("/quizzes");

this.$toast.success("Logged in successfully.", {position: "bottom-left", durati


on: 1000});

} catch (err) {

console.log(err); // Inspect error structure

let errorMessage = "Authentication failed!";

if (err.response?.data?.details) {

errorMessage = err.response.data.details; // Use the details field

} else if (err.response) {

switch (err.response.status) {

case 404:

errorMessage = "Email not found. Please check your email or register.";

break;

case 401:

errorMessage = "Invalid password. Please try again.";

break;

case 500:

errorMessage = "Server error. Please try again later.";

break;

default:
errorMessage = "An unexpected error occurred.";

this.$toast.error(errorMessage, {

position: "bottom-left",

duration: 2000

});

User Interface Design

 Clean and modern UI with consistent styling


 Intuitive form layout with icons
 Responsive feedback through toast notifications
 Visual feedback on input interactions (hover, focus states)

Error Handling

 Detailed error messages for different scenarios


 User-friendly error notifications
 Proper HTTP status code handling

} catch (err) {

console.log(err); // Inspect error structure

let errorMessage = "Authentication failed!";

if (err.response?.data?.details) {

errorMessage = err.response.data.details; // Use the details field

} else if (err.response) {
switch (err.response.status) {

case 404:

errorMessage = "Email not found. Please check your email or register.";

break;

case 401:

errorMessage = "Invalid password. Please try again.";

break;

case 500:

errorMessage = "Server error. Please try again later.";

break;

default:

errorMessage = "An unexpected error occurred.";

this.$toast.error(errorMessage, {

position: "bottom-left",

duration: 2000

});

 Code Organization
 Well-structured component architecture
 Clean separation of concerns (template, script, styles)
 Reusable components (AuthCard)
 Proper use of Vue.js best practices
 User Experience
 Clear navigation flow
 Informative feedback messages
 Smooth transitions and animations
 Accessible form elements

Cons of Demo Web System:

 Security Considerations
 Console logging of errors in production should be removed
 Could benefit from rate limiting for login attempts
 Password strength requirements not visible
 Form Validation
 Basic HTML5 validation only (required attribute)
 No client-side validation for email format
 No password complexity requirements shown to users
 Accessibility Issues
 No ARIA labels for form elements
 Color contrast could be improved for some elements
 No keyboard navigation indicators
 Performance
 No loading states during authentication
 No client-side caching implementation
 Could benefit from lazy loading components
 User Experience Improvements Needed
 No password recovery/reset functionality
 No password visibility toggle
 No form persistence on failed attempts
 No progressive enhancement for older browsers
 Technical Debt
 Hard-coded color values instead of CSS variables
 Some duplicate CSS that could be consolidated
 Limited responsive design considerations
 No TypeScript implementation for better type safety

Conclusion

The report highlights several areas for improvement in terms of performance, user
experience, and technical debt. There are issues with keyboard navigation
indicators, loading states during authentication, and client-side caching. User
experience could be enhanced with password recovery/reset functionality,
password visibility toggle, and form persistence on failed attempts. Additionally,
implementing lazy loading components and progressive enhancement for older
browsers would be beneficial. Addressing technical debt such as hard-coded color
values, duplicate CSS, limited responsive design considerations, and lack of
TypeScript implementation for better type safety would also be advantageous.

Appendices

Weekly Lab tasks


Lab 1

Challenges:
a. Add a description key to our existing data object with the value
“A pair of warm, fuzzy socks”. Then display description using
an expression in an p element, underneath the h1.

b. Add a link to your data object, and use v-bind to sync it up with
an anchor tag in your HTML. Hint: you’ll be binding to the href
attribute.
c. Add an onSale property to the product’s data that is used to
conditionally render a span that says “On Sale!”

Lab 2

Challenges:
a. Add an array of sizes to the data and use v-for to display them in a
list.

b. Create a new button and method to decrement the value of cart.

c. When inStock is false, bind a class to the “Out of Stock” p tag that
adds text-decoration: line-through to that element.

d. Add a new boolean data property onSale and create a computed


property that takes brand, product and onSale and prints out a string
whenever onSale is true.

Lab 3
Challenges:
a. Create a new component for product-details with a prop of details.

b. Add a button that removes the product from the cart array by emitting
an event with the id of the product to be removed.
c. Add a question to the form: “Would you recommend this product”.
Then take in that response from the user via radio buttons of “yes” or
“no” and add it to the productReview object, with form validation.

Lab 4
NoSQL databases are a class of databases designed to handle unstructured, semi-
structured, or structured data with a high degree of flexibility. Unlike traditional
relational databases, NoSQL databases do not rely on a fixed schema or use SQL
as their primary query language. They are optimized for handling large-scale data,
distributed storage, and high-speed operations. NoSQL databases are categorized
into types such as document stores, key-value stores, column-family stores, and
graph databases, each tailored to specific use cases.

MongoDB is a widely-used NoSQL database that falls under the category of


document stores. It stores data in a JSON-like format called BSON (Binary JSON),
making it highly adaptable to changes in data structures. This schema-less nature
allows developers to easily modify the data model as applications evolve, making
MongoDB an excellent choice for agile development. MongoDB supports features
such as horizontal scaling (via sharding), high availability (via replication), and
ACID-compliant transactions (from version 4.0 onwards), ensuring reliability and
scalability.

One of MongoDB's core strengths is its querying capabilities, including a robust


aggregation framework for advanced data processing and transformation. It is
particularly suited for applications with dynamic and unstructured data, such as
content management systems, IoT applications, and real-time analytics platforms.
However, MongoDB's lack of built-in relationships (as seen in SQL databases)
may lead to data duplication, requiring developers to carefully design their data
models.

In summary, MongoDB provides flexibility, scalability, and performance for


modern applications, making it a preferred choice for many developers working
with complex or rapidly changing data.

Lab 5
Node.js servers are built on JavaScript's runtime environment, allowing developers to use
JavaScript for both frontend and backend development. The key distinguishing feature of
Node.js is its non-blocking, event-driven architecture.

At its core, Node.js uses an event loop to handle multiple requests concurrently without creating
new threads for each request. When a request comes in, instead of blocking the execution while
waiting for operations like file reading or database queries, Node.js registers callbacks and
continues processing other requests. When the operation completes, the callback is executed.

A basic Node.js server typically uses either the built-in 'http' module or popular frameworks like
Express.js. Express simplifies routing, middleware implementation, and request handling. Here's
a minimal example:

const express = require('express');


const app = express();

app.get('/', (req, res) => {


res.send('Hello World!');
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});

Node.js servers excel at handling many concurrent connections with minimal overhead, making
them ideal for real-time applications like chat systems or streaming services. However, they may
not be the best choice for CPU-intensive tasks due to their single-threaded nature.

Key components of Node.js servers include:

 Middleware for processing requests


 Route handlers for different endpoints
 Error handling mechanisms
 Database connections
 Static file serving
 Request/Response objects for handling HTTP communication

The Node Package Manager (npm) ecosystem provides thousands of modules that can be easily
integrated into Node.js servers, making it highly extensible and maintainable.

Lab 6
REST (Representational State Transfer) is an architectural style for designing networked
applications. RESTful APIs use HTTP methods to perform operations on resources, where each
resource is identified by a unique URL.

The core principles of RESTful APIs include:

1. Statelessness: Each request contains all information needed to process it. The server
doesn't store client session data between requests.
2. Resource-Based: Everything is treated as a resource (like users, products, or orders),
identified by unique URLs (endpoints). For example:

GET /api/users # Get all users


GET /api/users/123 # Get specific user
POST /api/users # Create new user
PUT /api/users/123 # Update user
DELETE /api/users/123 # Delete user

3. Standard HTTP Methods:

 GET: Retrieve data


 POST: Create new resources
 PUT/PATCH: Update existing resources
 DELETE: Remove resources

4. Representation: Resources can be represented in different formats (typically JSON or


XML). The client can specify the desired format using HTTP headers.

Best practices include:

 Using nouns for resources, not verbs


 Nesting resources to show relationships (e.g., /users/123/orders)
 Including proper status codes (200 for success, 404 for not found, etc.)
 Implementing pagination for large datasets
 Using query parameters for filtering and sorting

Example response structure:

{
"status": "success",
"data": {
"id": 123,
"name": "John Doe",
"email": "[email protected]"
},
"message": "User retrieved successfully"
}
RESTful APIs are widely used because they're simple to understand, scalable, and provide a
standardized way for applications to communicate over the internet.

Demo web system


1. Design
a. ERB of database schema
b. System architecture overview (technology used)

c. File hierarchy (folder structure)

 ├── frontend/
 │ ├── src/
 │ │ ├── assets/
 │ │ ├── services/
 │ │ │ ├── router.js
 │ │ │ └── store.js
 │ │ └── views/
 │ │ ├── HomeView.vue
 │ │ ├── LoginView.vue
 │ │ ├── RegisterView.vue
 │ │ ├── UserView.vue
 │ │ ├── UserEditView.vue
 │ │ ├── QuizView.vue
 │ │ ├── QuizTestView.vue
 │ │ ├── QuizCreationView.vue
 │ │ ├── QuizEditView.vue
 │ │ ├── DashboardView.vue
 │ │ └── LeaderboardView.vue
 │ ├── package.json
 │ └── package-lock.json
 │
 ├── backend/
 │ ├── model/
 │ │ ├── user.js
 │ │ └── quizModel.js
 │ ├── middleware/
 │ │ ├── authentication.js
 │ │ └── roleAuth.js
 │ ├── routes/
 │ │ └── index.js
 │ ├── public/
 │ │ └── uploads/
 │ │ └── avatars/
 │ ├── app.js
 │ └── package.json
 │

d. User’s interface (site map, wireframes)


2. Implementation
2a. User Authentication and Authorization
The application uses JWT-based authentication stored in httpOnly cookies. This is evident from:
Authentication Flow:
JWT tokens are created using a secret key and have a maximum age:

Log
out is handled by clearing the auth cookie:

Auth
orization:
The application uses middleware for role-based access control:
router.post('/quizzes/create',
requireLogin,
requireRole('teacher'),
async (req, res) => {
try {
const { title, category, description, questions } = req.body;
const quiz = new Quiz({
title,
category,
description,
questions,
creator: req.user._id
});
await quiz.save();
res.status(201).json(quiz);
} catch (error) {
res.status(400).json({ message: error.message });
}
});
This shows that certain routes (like quiz creation) require both authentication and specific role
(teacher).
2b. CRUD Operations
The application implements full CRUD operations for quizzes:
Create:
Teachers can create new quizzes with multiple questions:
router.post('/quizzes/create',
requireLogin,
requireRole('teacher'),
async (req, res) => {
try {
const { title, category, description, questions } = req.body;
const quiz = new Quiz({
title,
category,
description,
questions,
creator: req.user._id
});
await quiz.save();
res.status(201).json(quiz);
} catch (error) {
res.status(400).json({ message: error.message });
}
});
Read:
Get all quizzes with statistics:
router.get('/quizzes', async (req, res) => {
try {
const quizzes = await Quiz.find();
// Calculate statistics for each quiz
const quizzesWithStats = await Promise.all(quizzes.map(async (quiz) => {
const stats = await quiz.calculateStats();
const quizObj = quiz.toObject();
return {
...quizObj,
attempts: stats.attempts,
averageScore: stats.averageScore
};
}));
res.json(quizzesWithStats);
} catch (err) {
console.error('Error fetching quizzes:', err);
res.status(500).json({ message: 'Error fetching quizzes' });
}
});
Get single quiz:
router.get('/quizzes/:id', async (req, res) => {
try {
const quiz = await Quiz.findById(req.params.id);
if (!quiz) {
return res.status(404).json({ message: 'Quiz not found' });
}
const stats = await quiz.calculateStats();
const quizObj = quiz.toObject();
res.json({
...quizObj,
attempts: stats.attempts,
averageScore: stats.averageScore
});
} catch (err) {
console.error('Error fetching quiz:', err);
res.status(500).json({ message: 'Error fetching quiz' });
}
});
Update:
Update existing quizzes:
router.put('/quizzes/:id',
requireLogin,
requireRole('teacher'),
async (req, res) => {
try {
const { title, description, category, questions } = req.body;
const quiz = await Quiz.findById(req.params.id);
if (!quiz) {
return res.status(404).json({ message: 'Quiz not found' });
}
// Update quiz fields
quiz.title = title;
quiz.description = description;
quiz.category = category;
quiz.questions = questions;
await quiz.save();
res.json(quiz);
} catch (error) {
console.error('Error updating quiz:', error);
res.status(400).json({ message: 'Error updating quiz' });
}
});
Delete:
While not shown in the provided snippets, the API structure suggests delete functionality exists.
2c. Additional Functions
Quiz Statistics:
The system tracks quiz attempts and calculates statistics:
quizSchema.methods.calculateStats = async function() {
const users = await User.find({
'attempts.quizId': this._id
});
const attempts = users.reduce((acc, user) => {
return acc.concat(user.attempts.filter(attempt =>
attempt.quizId.toString() === this._id.toString()
));
}, []);
const totalAttempts = attempts.length;
const totalScore = attempts.reduce((sum, attempt) => sum + attempt.score, 0);

return {
attempts: totalAttempts,
averageScore: totalAttempts ? +(totalScore / totalAttempts).toFixed(1) : 0
};
};
Leaderboard System:
Implements a leaderboard showing top performers:
router.get('/leaderboard', async (req, res) => {
try {
const quizzes = await Quiz.find().lean();
const leaderboardData = await Promise.all(quizzes.map(async (quiz) => {
const users = await User.find({
'attempts.quizId': quiz._id
}).select('username attempts');
const attempts = users.reduce((acc, user) => {
return acc.concat(
user.attempts
.filter(attempt => attempt.quizId.toString() === quiz._id.toSt
ring())
.map(attempt => ({
username: user.username,
score: attempt.score,
timestamp: attempt.timestamp
}))
);
}, []);
// Sort attempts by score (descending) and take top 5
const topAttempts = attempts
.sort((a, b) => b.score - a.score)
.slice(0, 5);
return {
_id: quiz._id,
title: quiz.title,
category: quiz.category,
attempts: topAttempts,
totalAttempts: attempts.length,
averageScore: attempts.length
? +(attempts.reduce((sum, a) => sum + a.score, 0) / attempt
s.length).toFixed(1)
: 0
};
}));
res.json(leaderboardData);
} catch (err) {
File Upload:
Supports avatar uploads using multer:
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public/uploads/avatars')
},
filename: function (req, file, cb) {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9)
cb(null, uniqueSuffix + '-' + file.originalname)
}
});
const upload = multer({ storage: storage });
Progress Tracking:
Tracks user progress and scores:
router.post('/users/attempts', requireLogin, async (req, res) => {
try {
const userId = req.user._id;
const { quizId, score } = req.body;
// Add attempt to user's attempts array
req.user.attempts.push({ quizId, score });
await req.user.save();
res.status(200).json({ message: 'Attempt saved successfully' });
} catch (error) {
console.error('Error saving attempt:', error);
res.status(500).json({ message: 'Error saving attempt' });
}
});
Multiple Question Types:
The system supports different types of questions:
questions: [{
type: {
type: String,
enum: ['multiple_choice', 'text_answer'],
default: 'multiple_choice'
},
question: String,
options: [String],
correctAnswer: {
type: mongoose.Schema.Types.Mixed,
required: true
}
}],
Multiple choice
Text answer
These implementations follow RESTful practices and include proper error
handling, authentication checks, and data validation throughout the application.
3. Testing
4. Challenges and Solution

You might also like