# Sequelize Quick Reference Guide ## Common Operations for Interview Quiz App ### 1. Database Connection ```javascript // 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, dialect: 'mysql', pool: { max: 10, min: 0, acquire: 30000, idle: 10000 } } ); // Test connection sequelize.authenticate() .then(() => console.log('MySQL connected')) .catch(err => console.error('Unable to connect:', err)); module.exports = sequelize; ``` --- ### 2. User Operations #### Register New User ```javascript const bcrypt = require('bcrypt'); const { User } = require('../models'); const registerUser = async (userData) => { const hashedPassword = await bcrypt.hash(userData.password, 10); const user = await User.create({ username: userData.username, email: userData.email, password: hashedPassword, role: 'user' }); return user; }; ``` #### Login User ```javascript const loginUser = async (email, password) => { const user = await User.findOne({ where: { email }, attributes: ['id', 'username', 'email', 'password', 'role'] }); if (!user) { throw new Error('User not found'); } const isValid = await bcrypt.compare(password, user.password); if (!isValid) { throw new Error('Invalid credentials'); } return user; }; ``` #### Get User Dashboard ```javascript const getUserDashboard = async (userId) => { const user = await User.findByPk(userId, { attributes: [ 'id', 'username', 'email', 'totalQuizzes', 'totalQuestions', 'correctAnswers', 'streak' ], include: [ { model: QuizSession, where: { status: 'completed' }, required: false, limit: 10, order: [['completedAt', 'DESC']], include: [{ model: Category, attributes: ['name'] }] } ] }); return user; }; ``` --- ### 3. Category Operations #### Get All Categories ```javascript const getCategories = async () => { 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' ] ] } }); return categories; }; ``` #### Get Guest-Accessible Categories ```javascript const getGuestCategories = async () => { const categories = await Category.findAll({ where: { isActive: true, guestAccessible: true }, attributes: ['id', 'name', 'description', 'icon', 'publicQuestionCount'] }); return categories; }; ``` --- ### 4. Question Operations #### Create Question (Admin) ```javascript const createQuestion = async (questionData) => { const question = await Question.create({ question: questionData.question, type: questionData.type, categoryId: questionData.categoryId, difficulty: questionData.difficulty, options: JSON.stringify(questionData.options), // Store as JSON correctAnswer: questionData.correctAnswer, explanation: questionData.explanation, keywords: JSON.stringify(questionData.keywords), tags: JSON.stringify(questionData.tags), visibility: questionData.visibility || 'registered', isGuestAccessible: questionData.isGuestAccessible || false, createdBy: questionData.userId }); // Update category question count await Category.increment('questionCount', { where: { id: questionData.categoryId } }); return question; }; ``` #### Get Questions by Category ```javascript const getQuestionsByCategory = async (categoryId, options = {}) => { const { difficulty, limit = 10, visibility = 'registered' } = options; const where = { categoryId, isActive: true, visibility: { [Op.in]: visibility === 'guest' ? ['public'] : ['public', 'registered', 'premium'] } }; if (difficulty) { where.difficulty = difficulty; } const questions = await Question.findAll({ where, order: sequelize.random(), limit, include: [{ model: Category, attributes: ['name'] }] }); return questions; }; ``` #### Search Questions (Full-Text) ```javascript const searchQuestions = async (searchTerm) => { const questions = await Question.findAll({ where: { [Op.or]: [ sequelize.literal(`MATCH(question, explanation) AGAINST('${searchTerm}' IN NATURAL LANGUAGE MODE)`) ], isActive: true }, limit: 50 }); return questions; }; ``` --- ### 5. Quiz Session Operations #### Start Quiz Session ```javascript const startQuizSession = async (sessionData) => { const { userId, guestSessionId, categoryId, questionCount, difficulty } = sessionData; // Create quiz session const quizSession = await QuizSession.create({ userId: userId || null, guestSessionId: guestSessionId || null, isGuestSession: !userId, categoryId, totalQuestions: questionCount, status: 'in-progress', startTime: new Date() }); // Get random questions const questions = await Question.findAll({ where: { categoryId, difficulty: difficulty || { [Op.in]: ['easy', 'medium', 'hard'] }, isActive: true }, order: sequelize.random(), limit: questionCount }); // Create junction entries const sessionQuestions = questions.map((q, index) => ({ quizSessionId: quizSession.id, questionId: q.id, questionOrder: index + 1 })); await sequelize.models.QuizSessionQuestion.bulkCreate(sessionQuestions); return { quizSession, questions }; }; ``` #### Submit Answer ```javascript const submitAnswer = async (answerData) => { const { quizSessionId, questionId, userAnswer } = answerData; // Get question const question = await Question.findByPk(questionId); // Check if correct const isCorrect = question.correctAnswer === userAnswer; // Save answer const answer = await QuizAnswer.create({ quizSessionId, questionId, userAnswer, isCorrect, timeSpent: answerData.timeSpent || 0 }); // Update quiz session score if (isCorrect) { await QuizSession.increment('score', { where: { id: quizSessionId } }); } // Update question statistics await Question.increment('timesAttempted', { where: { id: questionId } }); return { isCorrect, correctAnswer: question.correctAnswer, explanation: question.explanation }; }; ``` #### Complete Quiz Session ```javascript const completeQuizSession = async (sessionId) => { const session = await QuizSession.findByPk(sessionId, { include: [ { model: QuizAnswer, include: [{ model: Question, attributes: ['id', 'question'] }] } ] }); // Calculate results const correctAnswers = session.QuizAnswers.filter(a => a.isCorrect).length; const percentage = (correctAnswers / session.totalQuestions) * 100; const timeTaken = Math.floor((new Date() - session.startTime) / 1000); // seconds // Update session await session.update({ status: 'completed', endTime: new Date(), completedAt: new Date() }); // Update user stats if not guest if (session.userId) { await User.increment({ totalQuizzes: 1, totalQuestions: session.totalQuestions, correctAnswers: correctAnswers }, { where: { id: session.userId } }); } return { score: session.score, totalQuestions: session.totalQuestions, percentage, timeTaken, correctAnswers, incorrectAnswers: session.totalQuestions - correctAnswers, results: session.QuizAnswers }; }; ``` --- ### 6. Guest Session Operations #### Create Guest Session ```javascript const createGuestSession = async (deviceId, ipAddress, userAgent) => { const guestId = `guest_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const sessionToken = jwt.sign({ guestId }, process.env.JWT_SECRET, { expiresIn: '24h' }); const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 hours const guestSession = await GuestSession.create({ guestId, deviceId, sessionToken, quizzesAttempted: 0, maxQuizzes: 3, expiresAt, ipAddress, userAgent }); return guestSession; }; ``` #### Check Guest Quiz Limit ```javascript const checkGuestQuizLimit = async (guestSessionId) => { const session = await GuestSession.findByPk(guestSessionId); if (!session) { throw new Error('Guest session not found'); } if (new Date() > session.expiresAt) { throw new Error('Guest session expired'); } const remainingQuizzes = session.maxQuizzes - session.quizzesAttempted; if (remainingQuizzes <= 0) { throw new Error('Guest quiz limit reached'); } return { remainingQuizzes, resetTime: session.expiresAt }; }; ``` --- ### 7. Bookmark Operations #### Add Bookmark ```javascript const addBookmark = async (userId, questionId) => { const bookmark = await sequelize.models.UserBookmark.create({ userId, questionId }); return bookmark; }; ``` #### Get User Bookmarks ```javascript const getUserBookmarks = async (userId) => { const user = await User.findByPk(userId, { include: [{ model: Question, as: 'bookmarks', through: { attributes: ['bookmarkedAt'] }, include: [{ model: Category, attributes: ['name'] }] }] }); return user.bookmarks; }; ``` #### Remove Bookmark ```javascript const removeBookmark = async (userId, questionId) => { await sequelize.models.UserBookmark.destroy({ where: { userId, questionId } }); }; ``` --- ### 8. Admin Operations #### Get System Statistics ```javascript const getSystemStats = async () => { const [totalUsers, activeUsers, totalQuizzes] = await Promise.all([ User.count(), User.count({ where: { lastLogin: { [Op.gte]: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) } } }), QuizSession.count({ where: { status: 'completed' } }) ]); const popularCategories = await Category.findAll({ attributes: [ 'id', 'name', [sequelize.fn('COUNT', sequelize.col('QuizSessions.id')), 'quizCount'] ], include: [{ model: QuizSession, attributes: [], required: false }], group: ['Category.id'], order: [[sequelize.literal('quizCount'), 'DESC']], limit: 5 }); const avgScore = await QuizSession.findOne({ attributes: [[sequelize.fn('AVG', sequelize.col('score')), 'avgScore']], where: { status: 'completed' } }); return { totalUsers, activeUsers, totalQuizzes, popularCategories, averageScore: avgScore.dataValues.avgScore || 0 }; }; ``` #### Update Guest Settings ```javascript const updateGuestSettings = async (settings, adminUserId) => { const guestSettings = await GuestSettings.findOne(); if (guestSettings) { await guestSettings.update({ ...settings, updatedBy: adminUserId }); } else { await GuestSettings.create({ ...settings, updatedBy: adminUserId }); } return guestSettings; }; ``` --- ### 9. Transaction Example ```javascript const { Op } = require('sequelize'); const convertGuestToUser = async (guestSessionId, userData) => { const t = await sequelize.transaction(); try { // Create user const user = await User.create(userData, { transaction: t }); // Get guest session const guestSession = await GuestSession.findByPk(guestSessionId, { transaction: t }); // Migrate quiz sessions await QuizSession.update( { userId: user.id, isGuestSession: false }, { where: { guestSessionId }, transaction: t } ); // Calculate stats const stats = await QuizSession.findAll({ where: { userId: user.id }, attributes: [ [sequelize.fn('COUNT', sequelize.col('id')), 'totalQuizzes'], [sequelize.fn('SUM', sequelize.col('score')), 'totalScore'] ], transaction: t }); // Update user stats await user.update({ totalQuizzes: stats[0].dataValues.totalQuizzes || 0 }, { transaction: t }); // Delete guest session await guestSession.destroy({ transaction: t }); await t.commit(); return user; } catch (error) { await t.rollback(); throw error; } }; ``` --- ### 10. Common Query Operators ```javascript const { Op } = require('sequelize'); // Examples of common operators const examples = { // Equals { status: 'completed' }, // Not equals { status: { [Op.ne]: 'abandoned' } }, // Greater than / Less than { score: { [Op.gte]: 80 } }, { createdAt: { [Op.lte]: new Date() } }, // Between { score: { [Op.between]: [50, 100] } }, // In array { difficulty: { [Op.in]: ['easy', 'medium'] } }, // Like (case-sensitive) { question: { [Op.like]: '%javascript%' } }, // Not null { userId: { [Op.not]: null } }, // OR condition { [Op.or]: [ { visibility: 'public' }, { isGuestAccessible: true } ] }, // AND condition { [Op.and]: [ { isActive: true }, { difficulty: 'hard' } ] } }; ``` --- ## Error Handling ```javascript const handleSequelizeError = (error) => { if (error.name === 'SequelizeUniqueConstraintError') { return { status: 400, message: 'Record already exists' }; } if (error.name === 'SequelizeValidationError') { return { status: 400, message: error.errors.map(e => e.message).join(', ') }; } if (error.name === 'SequelizeForeignKeyConstraintError') { return { status: 400, message: 'Invalid foreign key reference' }; } return { status: 500, message: 'Database error' }; }; ``` --- ## Best Practices 1. **Always use parameterized queries** (Sequelize does this by default) 2. **Use transactions for multi-step operations** 3. **Index frequently queried columns** 4. **Use eager loading to avoid N+1 queries** 5. **Limit result sets with pagination** 6. **Use connection pooling** 7. **Handle errors gracefully** 8. **Log slow queries in development** 9. **Use prepared statements** 10. **Validate input before database operations** --- Happy coding! 🚀