51 KiB
Technical Interview Quiz Application - MySQL + Express + Angular + Node
Project Overview
A comprehensive technical interview preparation platform where users can practice interview questions across multiple categories with different question types (Multiple Choice, True/False, and Written Answer).
Technology Stack
Frontend
- Angular (Latest version)
- TypeScript
- RxJS for state management
- Angular Material or Bootstrap for UI components
Backend
- Node.js with Express.js
- MySQL (8.0+) with Sequelize ORM
- JWT for authentication
- Express Validator for input validation
- bcrypt for password hashing
Additional Tools
- Docker for containerization (optional)
- Jest for testing
- GitHub Actions for CI/CD
Core Features & User Stories
1. User Authentication & Authorization
User Story 1.0: Guest User Access (NEW)
As a visitor
I want to try the quiz without registering
So that I can explore the platform before committing to sign up
Acceptance Criteria:
- Guest users can access limited quiz content without authentication
- Guest users can take up to 3 quizzes per day (configurable by admin)
- Guest users see only questions marked as "public/guest-accessible" by admin
- Guest progress is NOT saved permanently (stored in session/local storage)
- After quiz limit reached, prompt to register for unlimited access
- Guest sessions expire after 24 hours
- Guest users can see limited categories (admin-controlled)
- Banner/prompt encouraging registration is visible for guests
API Endpoint:
POST /api/guest/start-session
Body: {
deviceId: string (generated client-side)
}
Response: {
sessionToken: string (temporary),
guestId: string,
remainingQuizzes: number,
availableCategories: Category[],
restrictions: {
maxQuizzes: number,
questionsPerQuiz: number,
allowedFeatures: string[]
}
}
GET /api/guest/quiz-limit
Headers: { X-Guest-Token: <session-token> }
Response: {
remainingQuizzes: number,
resetTime: Date,
upgradePrompt: string
}
Database Schema:
-- guest_sessions table
CREATE TABLE guest_sessions (
id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
guest_id VARCHAR(100) UNIQUE NOT NULL,
device_id VARCHAR(255) NOT NULL,
session_token VARCHAR(500) NOT NULL,
quizzes_attempted INT DEFAULT 0,
max_quizzes INT DEFAULT 3,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP NOT NULL,
ip_address VARCHAR(45),
user_agent TEXT,
INDEX idx_guest_id (guest_id),
INDEX idx_session_token (session_token(255)),
INDEX idx_expires_at (expires_at)
);
Sequelize Model:
// models/GuestSession.js
const { DataTypes } = require('sequelize');
module.exports = (sequelize) => {
const GuestSession = sequelize.define('GuestSession', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
guestId: {
type: DataTypes.STRING(100),
unique: true,
allowNull: false,
field: 'guest_id'
},
deviceId: {
type: DataTypes.STRING(255),
allowNull: false,
field: 'device_id'
},
sessionToken: {
type: DataTypes.STRING(500),
allowNull: false,
field: 'session_token'
},
quizzesAttempted: {
type: DataTypes.INTEGER,
defaultValue: 0,
field: 'quizzes_attempted'
},
maxQuizzes: {
type: DataTypes.INTEGER,
defaultValue: 3,
field: 'max_quizzes'
},
expiresAt: {
type: DataTypes.DATE,
allowNull: false,
field: 'expires_at'
},
ipAddress: {
type: DataTypes.STRING(45),
field: 'ip_address'
},
userAgent: {
type: DataTypes.TEXT,
field: 'user_agent'
}
}, {
tableName: 'guest_sessions',
timestamps: true,
createdAt: 'created_at',
updatedAt: false,
indexes: [
{ fields: ['guest_id'] },
{ fields: ['session_token'] },
{ fields: ['expires_at'] }
]
});
return GuestSession;
};
User Story 1.1: User Registration
As a new user
I want to create an account with email and password
So that I can save my progress and track my performance with unlimited access
Acceptance Criteria:
- User can register with email, username, and password
- Password must be at least 8 characters with 1 uppercase, 1 lowercase, 1 number
- Email validation is performed
- Duplicate emails are rejected
- User receives a welcome email upon successful registration
- User is automatically logged in after registration
- Registration unlocks unlimited quizzes and all features
- If converting from guest, show summary of guest performance
API Endpoint:
POST /api/auth/register
Body: {
username: string,
email: string,
password: string,
guestSessionId?: string (optional, to migrate guest data)
}
Response: {
token: string,
user: { id, username, email, role },
upgradedFromGuest: boolean,
migratedStats?: { quizzesTaken, score }
}
User Story 1.2: User Login
As a registered user
I want to log in with my credentials
So that I can access my personalized dashboard
Acceptance Criteria:
- User can login with email and password
- Invalid credentials show appropriate error message
- Successful login redirects to dashboard
- JWT token is generated and stored
- Token expires after 24 hours
API Endpoint:
POST /api/auth/login
Body: {
email: string,
password: string
}
Response: {
token: string,
user: { id, username, email, stats }
}
User Story 1.3: User Logout
As a logged-in user
I want to log out securely
So that my account remains protected
Acceptance Criteria:
- User can click logout button
- JWT token is cleared from storage
- User is redirected to login page
- Session is terminated
2. Question Management
User Story 2.1: Browse Questions by Category
As a user
I want to browse questions by technology category
So that I can focus on specific topics
Acceptance Criteria:
- Display list of categories (Angular, Node.js, MongoDB, etc.)
- Show question count per category
- User can select a category to view questions
- Categories are filterable and searchable
API Endpoint:
GET /api/categories
Response: [{
id: string,
name: string,
description: string,
questionCount: number,
icon: string
}]
User Story 2.2: View Question Details
As a user
I want to view question details with all options
So that I can attempt to answer it
Acceptance Criteria:
- Question text is clearly displayed
- Question type (Multiple Choice, True/False, Written) is shown
- All answer options are visible (for MCQ and T/F)
- Input field for written answers
- Category and difficulty level are displayed
API Endpoint:
GET /api/questions/:id
Response: {
id: string,
question: string,
type: 'multiple' | 'trueFalse' | 'written',
category: string,
difficulty: 'easy' | 'medium' | 'hard',
options: string[],
correctAnswer: number | boolean | string,
explanation: string,
keywords: string[]
}
User Story 2.3: Submit Answer
As a user
I want to submit my answer and get immediate feedback
So that I can learn from my mistakes
Acceptance Criteria:
- User can submit their selected/written answer
- System validates the answer
- Correct/incorrect feedback is shown immediately
- Explanation is displayed after submission
- Score is updated in real-time
- Answer cannot be changed after submission
API Endpoint:
POST /api/quiz/submit
Body: {
questionId: string,
userAnswer: any,
quizSessionId: string
}
Response: {
isCorrect: boolean,
correctAnswer: any,
explanation: string,
score: number
}
3. Quiz Session Management
User Story 3.1: Start Quiz Session
As a user
I want to start a quiz session for a specific category
So that I can practice multiple questions in one session
Acceptance Criteria:
- User selects a category and number of questions
- Quiz session is created with unique ID
- Questions are randomly selected from category
- Timer starts (optional)
- Progress is tracked
API Endpoint:
POST /api/quiz/start
Body: {
categoryId: string,
questionCount: number,
difficulty?: string
}
Response: {
sessionId: string,
questions: Question[],
startTime: Date,
totalQuestions: number
}
User Story 3.2: Navigate Between Questions
As a user
I want to move to next/previous questions
So that I can control my quiz pace
Acceptance Criteria:
- Next button is enabled after answering
- Previous button shows answered questions (review mode)
- Progress bar shows current position
- Question counter displays (e.g., "5 of 10")
User Story 3.3: Complete Quiz Session
As a user
I want to see my final score and performance summary
So that I can evaluate my knowledge
Acceptance Criteria:
- Total score is displayed (X/Y correct)
- Percentage score is calculated
- Time taken is shown
- Performance message (Excellent, Good, Keep Practicing)
- Option to review incorrect answers
- Option to retake quiz or return to dashboard
API Endpoint:
POST /api/quiz/complete
Body: {
sessionId: string
}
Response: {
score: number,
totalQuestions: number,
percentage: number,
timeTaken: number,
correctAnswers: number,
incorrectAnswers: number,
results: [{
questionId: string,
isCorrect: boolean,
userAnswer: any
}]
}
4. User Dashboard & Analytics
User Story 4.1: View Personal Dashboard
As a user
I want to see my overall statistics and progress
So that I can track my improvement
Acceptance Criteria:
- Display total quizzes taken
- Show overall accuracy percentage
- List recent quiz sessions
- Display category-wise performance
- Show streak and achievements
- Visualize progress with charts
API Endpoint:
GET /api/users/:userId/dashboard
Response: {
totalQuizzes: number,
overallAccuracy: number,
totalQuestions: number,
recentSessions: Session[],
categoryStats: [{
category: string,
accuracy: number,
questionsAttempted: number
}],
streak: number,
achievements: Achievement[]
}
User Story 4.2: View Quiz History
As a user
I want to view my past quiz sessions
So that I can review my performance over time
Acceptance Criteria:
- List all completed quiz sessions
- Show date, category, score for each session
- Filter by category and date range
- Sort by score or date
- Click to view detailed results
API Endpoint:
GET /api/users/:userId/history?page=1&limit=10&category=Angular
Response: {
sessions: [{
id: string,
category: string,
score: number,
totalQuestions: number,
date: Date,
timeTaken: number
}],
totalCount: number,
currentPage: number
}
5. Admin Features
User Story 5.0: Configure Guest Access Settings (NEW)
As an admin
I want to configure what content guest users can access
So that I can control the freemium experience and encourage registrations
Acceptance Criteria:
- Admin can set maximum quizzes per day for guests (default: 3)
- Admin can set maximum questions per quiz for guests (default: 5)
- Admin can mark categories as "guest-accessible" or "registered-only"
- Admin can mark individual questions as "public" or "premium"
- Admin can set guest session expiry time (default: 24 hours)
- Admin can enable/disable guest access entirely
- Admin can view guest usage statistics
- Admin can customize upgrade prompts and messaging
- Settings are applied immediately without system restart
API Endpoint:
GET /api/admin/guest-settings
Headers: { Authorization: Bearer <admin-token> }
Response: {
guestAccessEnabled: boolean,
maxQuizzesPerDay: number,
maxQuestionsPerQuiz: number,
sessionExpiryHours: number,
publicCategories: string[],
upgradePromptMessage: string
}
PUT /api/admin/guest-settings
Headers: { Authorization: Bearer <admin-token> }
Body: {
guestAccessEnabled: boolean,
maxQuizzesPerDay: number,
maxQuestionsPerQuiz: number,
sessionExpiryHours: number,
publicCategories: string[],
upgradePromptMessage: string
}
Response: {
message: string,
updatedSettings: GuestSettings
}
PUT /api/admin/categories/:id/access
Headers: { Authorization: Bearer <admin-token> }
Body: {
guestAccessible: boolean
}
Response: {
message: string,
category: Category
}
PUT /api/admin/questions/:id/visibility
Headers: { Authorization: Bearer <admin-token> }
Body: {
isPublic: boolean,
visibility: 'public' | 'registered' | 'premium'
}
Response: {
message: string,
question: Question
}
Admin Dashboard UI Components:
// Guest Access Settings Panel
{
guestAccessEnabled: Toggle,
maxQuizzesPerDay: NumberInput (1-10),
maxQuestionsPerQuiz: NumberInput (3-15),
sessionExpiryHours: NumberInput (1-72),
publicCategories: MultiSelect,
upgradePromptMessage: TextArea,
guestStatistics: {
totalGuestSessions: number,
guestToUserConversion: percentage,
averageQuizzesTaken: number
}
}
User Story 5.1: Add New Question
As an admin
I want to add new questions to the database
So that users have more content to practice
Acceptance Criteria:
- Admin can access question management panel
- Form to input question details (text, type, category, options, answer, explanation)
- NEW: Admin can mark question visibility (public/registered/premium)
- NEW: Admin can set if question is available for guest users
- Validate all required fields
- Preview question before submission
- Success/error message after submission
API Endpoint:
POST /api/admin/questions
Headers: { Authorization: Bearer <admin-token> }
Body: {
question: string,
type: string,
category: string,
difficulty: string,
options: string[],
correctAnswer: any,
explanation: string,
keywords: string[],
visibility: 'public' | 'registered' | 'premium', // NEW
isGuestAccessible: boolean // NEW
}
Response: {
message: string,
questionId: string
}
User Story 5.2: Edit Existing Question
As an admin
I want to edit existing questions
So that I can correct mistakes or update content
Acceptance Criteria:
- Admin can search for questions
- Edit form is pre-filled with existing data
- Changes are validated
- Update confirmation is shown
- Version history is maintained
API Endpoint:
PUT /api/admin/questions/:id
Headers: { Authorization: Bearer <admin-token> }
Body: { ...updated fields }
Response: { message: string }
User Story 5.3: Delete Question
As an admin
I want to delete inappropriate or outdated questions
So that the question bank remains high quality
Acceptance Criteria:
- Admin can soft delete questions
- Confirmation dialog before deletion
- Deleted questions don't appear in quizzes
- Deletion is logged
- Can restore deleted questions
API Endpoint:
DELETE /api/admin/questions/:id
Headers: { Authorization: Bearer <admin-token> }
Response: { message: string }
User Story 5.4: View User Statistics
As an admin
I want to view system-wide user statistics
So that I can understand platform usage
Acceptance Criteria:
- Total registered users
- Active users (last 7 days)
- Total quiz sessions
- Most popular categories
- Average quiz scores
- User growth chart
API Endpoint:
GET /api/admin/statistics
Headers: { Authorization: Bearer <admin-token> }
Response: {
totalUsers: number,
activeUsers: number,
totalQuizzes: number,
popularCategories: Category[],
averageScore: number,
userGrowth: [{date: Date, count: number}]
}
6. Additional Features
User Story 6.1: Search Questions
As a user
I want to search for specific questions
So that I can practice targeted topics
Acceptance Criteria:
- Search bar in navigation
- Search by keyword in question text
- Filter by category and difficulty
- Display matching results with highlighting
- Empty state when no results found
API Endpoint:
GET /api/questions/search?q=angular&category=Angular&difficulty=medium
Response: {
results: Question[],
totalCount: number
}
User Story 6.2: Bookmark Questions
As a user
I want to bookmark difficult questions
So that I can review them later
Acceptance Criteria:
- Bookmark icon on each question
- Toggle bookmark on/off
- View all bookmarked questions
- Remove bookmarks
- Sync across devices
API Endpoint:
POST /api/users/:userId/bookmarks
Body: { questionId: string }
Response: { message: string }
GET /api/users/:userId/bookmarks
Response: { bookmarks: Question[] }
User Story 6.3: Share Quiz Results
As a user
I want to share my quiz results on social media
So that I can celebrate my achievements
Acceptance Criteria:
- Share button after quiz completion
- Generate shareable image/card with score
- Links to Twitter, LinkedIn, Facebook
- Copy link functionality
- Privacy option (public/private)
User Story 6.4: Dark Mode
As a user
I want to toggle between light and dark themes
So that I can use the app comfortably in different lighting
Acceptance Criteria:
- Theme toggle in settings
- Preference is saved in local storage
- Smooth transition between themes
- All UI elements support both themes
User Story 6.5: Mobile Responsive Design
As a user
I want to use the app on my mobile device
So that I can practice anywhere
Acceptance Criteria:
- Fully responsive layout (320px to 4K)
- Touch-friendly buttons and controls
- Readable text on small screens
- Optimized images and assets
- Works offline with service worker (PWA)
Database Schema
1. Users Table
CREATE TABLE users (
id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
role ENUM('user', 'admin') DEFAULT 'user',
profile_image VARCHAR(500),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
last_login TIMESTAMP NULL,
total_quizzes INT DEFAULT 0,
total_questions INT DEFAULT 0,
correct_answers INT DEFAULT 0,
streak INT DEFAULT 0,
INDEX idx_email (email),
INDEX idx_username (username),
INDEX idx_role (role)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Sequelize Model:
const { DataTypes } = require('sequelize');
module.exports = (sequelize) => {
const User = sequelize.define('User', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
username: {
type: DataTypes.STRING(50),
unique: true,
allowNull: false
},
email: {
type: DataTypes.STRING(255),
unique: true,
allowNull: false,
validate: { isEmail: true }
},
password: {
type: DataTypes.STRING(255),
allowNull: false
},
role: {
type: DataTypes.ENUM('user', 'admin'),
defaultValue: 'user'
},
profileImage: {
type: DataTypes.STRING(500),
field: 'profile_image'
},
lastLogin: {
type: DataTypes.DATE,
field: 'last_login'
},
totalQuizzes: {
type: DataTypes.INTEGER,
defaultValue: 0,
field: 'total_quizzes'
},
totalQuestions: {
type: DataTypes.INTEGER,
defaultValue: 0,
field: 'total_questions'
},
correctAnswers: {
type: DataTypes.INTEGER,
defaultValue: 0,
field: 'correct_answers'
},
streak: {
type: DataTypes.INTEGER,
defaultValue: 0
}
}, {
tableName: 'users',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at'
});
User.associate = (models) => {
User.belongsToMany(models.Question, {
through: 'user_bookmarks',
as: 'bookmarks',
foreignKey: 'user_id'
});
User.hasMany(models.QuizSession, { foreignKey: 'user_id' });
};
return User;
};
2. Categories Table
CREATE TABLE categories (
id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
name VARCHAR(100) UNIQUE NOT NULL,
description TEXT,
icon VARCHAR(255),
slug VARCHAR(100) UNIQUE NOT NULL,
question_count INT DEFAULT 0,
is_active BOOLEAN DEFAULT TRUE,
guest_accessible BOOLEAN DEFAULT FALSE,
public_question_count INT DEFAULT 0,
registered_question_count INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_slug (slug),
INDEX idx_is_active (is_active),
INDEX idx_guest_accessible (guest_accessible)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Sequelize Model:
const { DataTypes } = require('sequelize');
module.exports = (sequelize) => {
const Category = sequelize.define('Category', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
name: {
type: DataTypes.STRING(100),
unique: true,
allowNull: false
},
description: {
type: DataTypes.TEXT
},
icon: {
type: DataTypes.STRING(255)
},
slug: {
type: DataTypes.STRING(100),
unique: true,
allowNull: false
},
questionCount: {
type: DataTypes.INTEGER,
defaultValue: 0,
field: 'question_count'
},
isActive: {
type: DataTypes.BOOLEAN,
defaultValue: true,
field: 'is_active'
},
guestAccessible: {
type: DataTypes.BOOLEAN,
defaultValue: false,
field: 'guest_accessible'
},
publicQuestionCount: {
type: DataTypes.INTEGER,
defaultValue: 0,
field: 'public_question_count'
},
registeredQuestionCount: {
type: DataTypes.INTEGER,
defaultValue: 0,
field: 'registered_question_count'
}
}, {
tableName: 'categories',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at'
});
Category.associate = (models) => {
Category.hasMany(models.Question, { foreignKey: 'category_id' });
};
return Category;
};
3. Questions Table
CREATE TABLE questions (
id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
question TEXT NOT NULL,
type ENUM('multiple', 'trueFalse', 'written') NOT NULL,
category_id CHAR(36) NOT NULL,
difficulty ENUM('easy', 'medium', 'hard') NOT NULL,
options JSON,
correct_answer VARCHAR(500),
explanation TEXT NOT NULL,
keywords JSON,
tags JSON,
visibility ENUM('public', 'registered', 'premium') DEFAULT 'registered',
is_guest_accessible BOOLEAN DEFAULT FALSE,
created_by CHAR(36),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
is_active BOOLEAN DEFAULT TRUE,
times_attempted INT DEFAULT 0,
correct_rate DECIMAL(5,2) DEFAULT 0.00,
FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE RESTRICT,
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL,
INDEX idx_category (category_id),
INDEX idx_difficulty (difficulty),
INDEX idx_type (type),
INDEX idx_visibility (visibility),
INDEX idx_is_active (is_active),
INDEX idx_is_guest_accessible (is_guest_accessible),
FULLTEXT idx_question_text (question, explanation)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Sequelize Model:
const { DataTypes } = require('sequelize');
module.exports = (sequelize) => {
const Question = sequelize.define('Question', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
question: {
type: DataTypes.TEXT,
allowNull: false
},
type: {
type: DataTypes.ENUM('multiple', 'trueFalse', 'written'),
allowNull: false
},
categoryId: {
type: DataTypes.UUID,
allowNull: false,
field: 'category_id'
},
difficulty: {
type: DataTypes.ENUM('easy', 'medium', 'hard'),
allowNull: false
},
options: {
type: DataTypes.JSON,
comment: 'Array of answer options for multiple choice questions'
},
correctAnswer: {
type: DataTypes.STRING(500),
field: 'correct_answer'
},
explanation: {
type: DataTypes.TEXT,
allowNull: false
},
keywords: {
type: DataTypes.JSON,
comment: 'Array of keywords for search'
},
tags: {
type: DataTypes.JSON,
comment: 'Array of tags'
},
visibility: {
type: DataTypes.ENUM('public', 'registered', 'premium'),
defaultValue: 'registered'
},
isGuestAccessible: {
type: DataTypes.BOOLEAN,
defaultValue: false,
field: 'is_guest_accessible'
},
createdBy: {
type: DataTypes.UUID,
field: 'created_by'
},
isActive: {
type: DataTypes.BOOLEAN,
defaultValue: true,
field: 'is_active'
},
timesAttempted: {
type: DataTypes.INTEGER,
defaultValue: 0,
field: 'times_attempted'
},
correctRate: {
type: DataTypes.DECIMAL(5, 2),
defaultValue: 0.00,
field: 'correct_rate'
}
}, {
tableName: 'questions',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at'
});
Question.associate = (models) => {
Question.belongsTo(models.Category, { foreignKey: 'category_id' });
Question.belongsTo(models.User, { foreignKey: 'created_by', as: 'creator' });
Question.belongsToMany(models.User, {
through: 'user_bookmarks',
as: 'bookmarkedBy',
foreignKey: 'question_id'
});
};
return Question;
};
4. Quiz Sessions Table
CREATE TABLE quiz_sessions (
id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
user_id CHAR(36),
guest_session_id CHAR(36),
is_guest_session BOOLEAN DEFAULT FALSE,
category_id CHAR(36),
start_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
end_time TIMESTAMP NULL,
score INT DEFAULT 0,
total_questions INT NOT NULL,
status ENUM('in-progress', 'completed', 'abandoned') DEFAULT 'in-progress',
completed_at TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (guest_session_id) REFERENCES guest_sessions(id) ON DELETE CASCADE,
FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE SET NULL,
INDEX idx_user_id (user_id),
INDEX idx_guest_session_id (guest_session_id),
INDEX idx_status (status),
INDEX idx_completed_at (completed_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Sequelize Model:
const { DataTypes } = require('sequelize');
module.exports = (sequelize) => {
const QuizSession = sequelize.define('QuizSession', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
userId: {
type: DataTypes.UUID,
field: 'user_id'
},
guestSessionId: {
type: DataTypes.UUID,
field: 'guest_session_id'
},
isGuestSession: {
type: DataTypes.BOOLEAN,
defaultValue: false,
field: 'is_guest_session'
},
categoryId: {
type: DataTypes.UUID,
field: 'category_id'
},
startTime: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
field: 'start_time'
},
endTime: {
type: DataTypes.DATE,
field: 'end_time'
},
score: {
type: DataTypes.INTEGER,
defaultValue: 0
},
totalQuestions: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'total_questions'
},
status: {
type: DataTypes.ENUM('in-progress', 'completed', 'abandoned'),
defaultValue: 'in-progress'
},
completedAt: {
type: DataTypes.DATE,
field: 'completed_at'
}
}, {
tableName: 'quiz_sessions',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at'
});
QuizSession.associate = (models) => {
QuizSession.belongsTo(models.User, { foreignKey: 'user_id' });
QuizSession.belongsTo(models.GuestSession, { foreignKey: 'guest_session_id' });
QuizSession.belongsTo(models.Category, { foreignKey: 'category_id' });
QuizSession.belongsToMany(models.Question, {
through: 'quiz_session_questions',
foreignKey: 'quiz_session_id'
});
QuizSession.hasMany(models.QuizAnswer, { foreignKey: 'quiz_session_id' });
};
return QuizSession;
};
5. Quiz Session Questions (Junction Table)
CREATE TABLE quiz_session_questions (
id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
quiz_session_id CHAR(36) NOT NULL,
question_id CHAR(36) NOT NULL,
question_order INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (quiz_session_id) REFERENCES quiz_sessions(id) ON DELETE CASCADE,
FOREIGN KEY (question_id) REFERENCES questions(id) ON DELETE CASCADE,
INDEX idx_quiz_session (quiz_session_id),
INDEX idx_question (question_id),
UNIQUE KEY unique_session_question (quiz_session_id, question_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
6. Quiz Answers Table
CREATE TABLE quiz_answers (
id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
quiz_session_id CHAR(36) NOT NULL,
question_id CHAR(36) NOT NULL,
user_answer TEXT,
is_correct BOOLEAN,
time_spent INT COMMENT 'Time in seconds',
answered_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (quiz_session_id) REFERENCES quiz_sessions(id) ON DELETE CASCADE,
FOREIGN KEY (question_id) REFERENCES questions(id) ON DELETE CASCADE,
INDEX idx_quiz_session (quiz_session_id),
INDEX idx_question (question_id),
INDEX idx_is_correct (is_correct)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Sequelize Model:
const { DataTypes } = require('sequelize');
module.exports = (sequelize) => {
const QuizAnswer = sequelize.define('QuizAnswer', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
quizSessionId: {
type: DataTypes.UUID,
allowNull: false,
field: 'quiz_session_id'
},
questionId: {
type: DataTypes.UUID,
allowNull: false,
field: 'question_id'
},
userAnswer: {
type: DataTypes.TEXT,
field: 'user_answer'
},
isCorrect: {
type: DataTypes.BOOLEAN,
field: 'is_correct'
},
timeSpent: {
type: DataTypes.INTEGER,
comment: 'Time in seconds',
field: 'time_spent'
},
answeredAt: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
field: 'answered_at'
}
}, {
tableName: 'quiz_answers',
timestamps: false
});
QuizAnswer.associate = (models) => {
QuizAnswer.belongsTo(models.QuizSession, { foreignKey: 'quiz_session_id' });
QuizAnswer.belongsTo(models.Question, { foreignKey: 'question_id' });
};
return QuizAnswer;
};
7. Guest Settings Table
CREATE TABLE guest_settings (
id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
guest_access_enabled BOOLEAN DEFAULT TRUE,
max_quizzes_per_day INT DEFAULT 3,
max_questions_per_quiz INT DEFAULT 5,
session_expiry_hours INT DEFAULT 24,
upgrade_prompt_message TEXT,
feature_restrictions JSON COMMENT 'JSON object with feature flags',
updated_by CHAR(36),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (updated_by) REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Sequelize Model:
const { DataTypes } = require('sequelize');
module.exports = (sequelize) => {
const GuestSettings = sequelize.define('GuestSettings', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
guestAccessEnabled: {
type: DataTypes.BOOLEAN,
defaultValue: true,
field: 'guest_access_enabled'
},
maxQuizzesPerDay: {
type: DataTypes.INTEGER,
defaultValue: 3,
field: 'max_quizzes_per_day'
},
maxQuestionsPerQuiz: {
type: DataTypes.INTEGER,
defaultValue: 5,
field: 'max_questions_per_quiz'
},
sessionExpiryHours: {
type: DataTypes.INTEGER,
defaultValue: 24,
field: 'session_expiry_hours'
},
upgradePromptMessage: {
type: DataTypes.TEXT,
field: 'upgrade_prompt_message'
},
featureRestrictions: {
type: DataTypes.JSON,
defaultValue: {
canBookmark: false,
canViewHistory: false,
canViewExplanations: true,
canShareResults: false
},
field: 'feature_restrictions'
},
updatedBy: {
type: DataTypes.UUID,
field: 'updated_by'
}
}, {
tableName: 'guest_settings',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at'
});
GuestSettings.associate = (models) => {
GuestSettings.belongsTo(models.User, { foreignKey: 'updated_by', as: 'updater' });
};
return GuestSettings;
};
8. Guest Settings Categories (Junction Table)
CREATE TABLE guest_settings_categories (
id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
guest_settings_id CHAR(36) NOT NULL,
category_id CHAR(36) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (guest_settings_id) REFERENCES guest_settings(id) ON DELETE CASCADE,
FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE CASCADE,
INDEX idx_guest_settings (guest_settings_id),
INDEX idx_category (category_id),
UNIQUE KEY unique_settings_category (guest_settings_id, category_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
9. Achievements Table
CREATE TABLE achievements (
id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
name VARCHAR(100) NOT NULL,
description TEXT,
icon VARCHAR(255),
condition_type VARCHAR(50) NOT NULL COMMENT 'e.g., streak, score, category',
condition_value INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_condition_type (condition_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Sequelize Model:
const { DataTypes } = require('sequelize');
module.exports = (sequelize) => {
const Achievement = sequelize.define('Achievement', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
name: {
type: DataTypes.STRING(100),
allowNull: false
},
description: {
type: DataTypes.TEXT
},
icon: {
type: DataTypes.STRING(255)
},
conditionType: {
type: DataTypes.STRING(50),
allowNull: false,
field: 'condition_type'
},
conditionValue: {
type: DataTypes.INTEGER,
allowNull: false,
field: 'condition_value'
}
}, {
tableName: 'achievements',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at'
});
Achievement.associate = (models) => {
Achievement.belongsToMany(models.User, {
through: 'user_achievements',
foreignKey: 'achievement_id',
as: 'earnedBy'
});
};
return Achievement;
};
10. User Achievements (Junction Table)
CREATE TABLE user_achievements (
id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
user_id CHAR(36) NOT NULL,
achievement_id CHAR(36) NOT NULL,
earned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (achievement_id) REFERENCES achievements(id) ON DELETE CASCADE,
INDEX idx_user (user_id),
INDEX idx_achievement (achievement_id),
UNIQUE KEY unique_user_achievement (user_id, achievement_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
11. User Bookmarks (Junction Table)
CREATE TABLE user_bookmarks (
id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
user_id CHAR(36) NOT NULL,
question_id CHAR(36) NOT NULL,
bookmarked_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (question_id) REFERENCES questions(id) ON DELETE CASCADE,
INDEX idx_user (user_id),
INDEX idx_question (question_id),
UNIQUE KEY unique_user_bookmark (user_id, question_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
API Endpoints Summary
Authentication
POST /api/auth/register- Register new user (with optional guest migration)POST /api/auth/login- Login userPOST /api/auth/logout- Logout userGET /api/auth/verify- Verify JWT tokenPOST /api/auth/forgot-password- Request password resetPOST /api/auth/reset-password- Reset password with token
Guest Access (NEW)
POST /api/guest/start-session- Create guest sessionGET /api/guest/quiz-limit- Check remaining quizzesGET /api/guest/available-categories- Get guest-accessible categoriesPOST /api/guest/quiz/start- Start guest quiz sessionGET /api/guest/session/:sessionId- Get guest session detailsPOST /api/guest/convert- Convert guest to registered user
Users
GET /api/users/:userId- Get user profilePUT /api/users/:userId- Update user profileGET /api/users/:userId/dashboard- Get dashboard dataGET /api/users/:userId/history- Get quiz historyPOST /api/users/:userId/bookmarks- Add bookmarkDELETE /api/users/:userId/bookmarks/:questionId- Remove bookmarkGET /api/users/:userId/bookmarks- Get all bookmarks
Categories
GET /api/categories- Get all categoriesGET /api/categories/:id- Get category detailsPOST /api/categories- Create category (admin)PUT /api/categories/:id- Update category (admin)DELETE /api/categories/:id- Delete category (admin)
Questions
GET /api/questions- Get all questions (paginated)GET /api/questions/:id- Get question by IDGET /api/questions/search- Search questionsGET /api/questions/category/:categoryId- Get questions by categoryPOST /api/questions- Create question (admin)PUT /api/questions/:id- Update question (admin)DELETE /api/questions/:id- Delete question (admin)
Quiz
POST /api/quiz/start- Start new quiz sessionPOST /api/quiz/submit- Submit answerGET /api/quiz/session/:sessionId- Get session detailsPOST /api/quiz/complete- Complete quiz sessionGET /api/quiz/review/:sessionId- Review completed quiz
Admin
GET /api/admin/statistics- Get system statisticsGET /api/admin/users- Get all usersPUT /api/admin/users/:userId/role- Update user roleGET /api/admin/reports- Get usage reportsGET /api/admin/guest-settings- Get guest access settings (NEW)PUT /api/admin/guest-settings- Update guest access settings (NEW)PUT /api/admin/categories/:id/access- Set category guest accessibility (NEW)PUT /api/admin/questions/:id/visibility- Set question visibility level (NEW)GET /api/admin/guest-analytics- Get guest user analytics (NEW)GET /api/admin/conversion-rate- Get guest-to-user conversion metrics (NEW)
Non-Functional Requirements
Performance
- API response time < 200ms for 95% of requests
- Support 1000+ concurrent users
- Database query optimization with proper indexing
- Implement caching for frequently accessed data (Redis)
- Use MySQL query cache and InnoDB buffer pool optimization
- Connection pooling with Sequelize
Security
- HTTPS only
- JWT token authentication
- Password hashing with bcrypt (10 rounds)
- Input validation and sanitization
- Rate limiting on API endpoints
- CORS configuration
- SQL injection prevention (Sequelize parameterized queries)
- XSS protection
- Prepared statements for all database queries
Scalability
- Horizontal scaling support
- Load balancing
- Database sharding (future)
- CDN for static assets
- Microservices architecture (future)
Accessibility
- WCAG 2.1 Level AA compliance
- Keyboard navigation
- Screen reader support
- Color contrast ratios
- Alt text for images
Testing
- Unit tests (80%+ coverage)
- Integration tests for APIs
- E2E tests for critical user flows
- Performance testing
Development Phases
Phase 1: MVP (Weeks 1-4)
- Guest access with limited quiz functionality (NEW)
- Admin guest settings configuration panel (NEW)
- User authentication (register, login, logout)
- Basic question display with visibility controls
- Quiz session management (guest + registered)
- Simple dashboard with statistics
- Admin panel for question CRUD with visibility settings
Phase 2: Enhanced Features (Weeks 5-8)
- Category management
- Advanced filtering and search
- Bookmarking system
- Quiz history with detailed analytics
- Performance charts and visualizations
Phase 3: Social & Gamification (Weeks 9-12)
- Achievements and badges
- Leaderboards
- Share results on social media
- User profiles with avatars
- Daily challenges
Phase 4: Advanced Features (Weeks 13-16)
- AI-powered question recommendations
- Timed quizzes
- Code editor for programming questions
- Discussion forum
- Email notifications
Deployment Strategy
Development Environment
- Local MySQL 8.0+ instance
- Node.js v18+
- Sequelize CLI for migrations
- Angular CLI
- VS Code / WebStorm
- MySQL Workbench (optional GUI)
Staging Environment
- Managed MySQL (AWS RDS, Azure Database, or PlanetScale)
- Heroku / Railway / Render
- CI/CD with GitHub Actions
- Automated database migrations
Production Environment
- Managed MySQL with read replicas (AWS RDS, Azure Database for MySQL)
- AWS / DigitalOcean / Azure
- Load balancer
- SSL certificate
- Connection pooling (ProxySQL or built-in)
- Monitoring (New Relic / DataDog)
- Automated backup strategy (daily snapshots)
- Point-in-time recovery enabled
- Disaster recovery plan
Success Metrics
User Engagement
- Daily Active Users (DAU) - including guest users
- Monthly Active Users (MAU) - including guest users
- Guest-to-Registered User Conversion Rate (NEW)
- Average quizzes taken before registration (NEW)
- Guest user bounce rate (NEW)
- Average session duration
- Questions answered per session
- Return rate
- Registration trigger points analysis (NEW)
Performance
- API response time
- Error rate
- Uptime percentage
- Page load time
Business
- User registration rate
- Quiz completion rate
- User retention rate
- NPS score
Future Enhancements
- AI Integration: Personalized learning paths based on user performance
- Video Explanations: Video tutorials for complex topics
- Live Quizzes: Compete with other users in real-time
- Certifications: Issue certificates on completion
- Company-Specific Prep: Custom question sets for specific companies
- Interview Scheduler: Book mock interviews with mentors
- Multi-language Support: i18n for global reach
- Offline Mode: Progressive Web App capabilities
- Voice Commands: Practice with voice-based interaction
- Collaborative Learning: Study groups and peer review
Getting Started (For Developers)
Prerequisites
# Install Node.js v18+
node --version
# Install MySQL 8.0+
# Windows: Download from https://dev.mysql.com/downloads/mysql/
# Mac: brew install mysql
# Linux: sudo apt-get install mysql-server
# Install Angular CLI
npm install -g @angular/cli
# Install Sequelize CLI
npm install -g sequelize-cli
Backend Setup
cd backend
npm install
# Install MySQL dependencies
npm install sequelize mysql2
# Create .env file
cp .env.example .env
# Configure MySQL connection in .env
# DB_HOST=localhost
# DB_PORT=3306
# DB_NAME=interview_quiz_db
# DB_USER=your_username
# DB_PASSWORD=your_password
# DB_DIALECT=mysql
# Create database
mysql -u root -p
CREATE DATABASE interview_quiz_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
EXIT;
# Run migrations
npx sequelize-cli db:migrate
# Seed database (optional)
npx sequelize-cli db:seed:all
# Start development server
npm run dev
Frontend Setup
cd frontend
npm install
ng serve
Database Migrations
# Create a new migration
npx sequelize-cli migration:generate --name migration-name
# Run migrations
npx sequelize-cli db:migrate
# Undo last migration
npx sequelize-cli db:migrate:undo
# Undo all migrations
npx sequelize-cli db:migrate:undo:all
# Create seeder
npx sequelize-cli seed:generate --name demo-data
Running Tests
# Backend tests
cd backend && npm test
# Frontend tests
cd frontend && ng test
# Integration tests with test database
DB_NAME=interview_quiz_test npm test
Contributing Guidelines
- Fork the repository
- Create feature branch (
git checkout -b feature/amazing-feature) - Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing-feature) - Open Pull Request
License
MIT License
Contact & Support
- Email: support@interviewquiz.com
- Documentation: https://docs.interviewquiz.com
- GitHub: https://github.com/yourorg/interview-quiz
MySQL Configuration Best Practices
Connection Pool Configuration
// config/database.js
const { Sequelize } = require('sequelize');
const sequelize = new Sequelize(
process.env.DB_NAME,
process.env.DB_USER,
process.env.DB_PASSWORD,
{
host: process.env.DB_HOST,
port: process.env.DB_PORT || 3306,
dialect: 'mysql',
logging: process.env.NODE_ENV === 'development' ? console.log : false,
pool: {
max: 10,
min: 0,
acquire: 30000,
idle: 10000
},
dialectOptions: {
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci',
connectTimeout: 10000
},
define: {
timestamps: true,
underscored: true,
freezeTableName: true
}
}
);
module.exports = sequelize;
MySQL Optimization Settings
-- my.cnf or my.ini configuration
[mysqld]
# InnoDB settings
innodb_buffer_pool_size = 2G
innodb_log_file_size = 512M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
# Connection settings
max_connections = 200
max_connect_errors = 100
connect_timeout = 10
wait_timeout = 600
interactive_timeout = 600
# Query cache (MySQL 5.7)
query_cache_type = 1
query_cache_size = 256M
# Character set
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
# Binary logging (for replication)
log_bin = mysql-bin
binlog_format = ROW
expire_logs_days = 7
# Slow query log
slow_query_log = 1
long_query_time = 2
Sequelize Migrations Structure
backend/
├── config/
│ └── database.js
├── migrations/
│ ├── 20250101000001-create-users.js
│ ├── 20250101000002-create-categories.js
│ ├── 20250101000003-create-questions.js
│ ├── 20250101000004-create-guest-sessions.js
│ ├── 20250101000005-create-quiz-sessions.js
│ ├── 20250101000006-create-quiz-answers.js
│ ├── 20250101000007-create-achievements.js
│ ├── 20250101000008-create-guest-settings.js
│ └── 20250101000009-create-junction-tables.js
├── models/
│ ├── index.js
│ ├── User.js
│ ├── Category.js
│ ├── Question.js
│ ├── GuestSession.js
│ ├── QuizSession.js
│ ├── QuizAnswer.js
│ ├── Achievement.js
│ └── GuestSettings.js
├── seeders/
│ ├── 20250101000001-demo-users.js
│ ├── 20250101000002-demo-categories.js
│ └── 20250101000003-demo-questions.js
└── .sequelizerc
Sample .sequelizerc Configuration
const path = require('path');
module.exports = {
'config': path.resolve('config', 'database.js'),
'models-path': path.resolve('models'),
'seeders-path': path.resolve('seeders'),
'migrations-path': path.resolve('migrations')
};
Database Indexing Strategy
-- Performance Indexes for Common Queries
-- User searches
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_username ON users(username);
-- Question searches with full-text
CREATE FULLTEXT INDEX idx_questions_fulltext ON questions(question, explanation);
-- Quiz session queries
CREATE INDEX idx_quiz_sessions_user_status ON quiz_sessions(user_id, status, completed_at);
CREATE INDEX idx_quiz_sessions_guest ON quiz_sessions(guest_session_id, status);
-- Performance tracking
CREATE INDEX idx_quiz_answers_correct ON quiz_answers(is_correct, answered_at);
CREATE INDEX idx_quiz_answers_session_question ON quiz_answers(quiz_session_id, question_id);
-- Category filtering
CREATE INDEX idx_questions_category_active ON questions(category_id, is_active, visibility);
-- Guest session cleanup
CREATE INDEX idx_guest_sessions_expires ON guest_sessions(expires_at);
-- Composite indexes for dashboard queries
CREATE INDEX idx_users_stats ON users(total_quizzes, correct_answers, streak);
Query Optimization Examples
// Efficient category listing with question counts
const categories = await Category.findAll({
where: { isActive: true },
attributes: {
include: [
[
sequelize.literal(`(
SELECT COUNT(*)
FROM questions
WHERE questions.category_id = categories.id
AND questions.is_active = true
)`),
'questionCount'
]
]
}
});
// Dashboard stats with single query
const userDashboard = await User.findByPk(userId, {
include: [
{
model: QuizSession,
where: { status: 'completed' },
required: false,
limit: 10,
order: [['completed_at', 'DESC']],
include: [{ model: Category }]
}
]
});
// Efficient bookmark management
const bookmarkedQuestions = await Question.findAll({
include: [{
model: User,
as: 'bookmarkedBy',
where: { id: userId },
through: { attributes: [] }
}],
where: { isActive: true }
});
Backup Strategy
# Daily backup script
#!/bin/bash
BACKUP_DIR="/var/backups/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
DB_NAME="interview_quiz_db"
# Create backup directory
mkdir -p $BACKUP_DIR
# Backup database
mysqldump -u root -p$MYSQL_PASSWORD \
--single-transaction \
--routines \
--triggers \
--events \
$DB_NAME | gzip > $BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz
# Delete backups older than 30 days
find $BACKUP_DIR -name "*.sql.gz" -mtime +30 -delete
echo "Backup completed: ${DB_NAME}_${DATE}.sql.gz"
Environment Variables (.env.example)
# Application
NODE_ENV=development
PORT=3000
API_PREFIX=/api
# Database
DB_HOST=localhost
DB_PORT=3306
DB_NAME=interview_quiz_db
DB_USER=root
DB_PASSWORD=your_secure_password
DB_DIALECT=mysql
# JWT
JWT_SECRET=your_jwt_secret_key_here
JWT_EXPIRE=24h
# Redis (for caching)
REDIS_HOST=localhost
REDIS_PORT=6379
# Email (for notifications)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your_email@gmail.com
SMTP_PASSWORD=your_email_password
# Frontend URL
FRONTEND_URL=http://localhost:4200
# Rate Limiting
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX_REQUESTS=100
End of User Stories Document
Version 2.0 - MySQL Edition - Last Updated: November 2025