# Sample Sequelize Migration Files ## Migration 1: Create Users Table **File**: `migrations/20250101000001-create-users.js` ```javascript 'use strict'; module.exports = { up: async (queryInterface, Sequelize) => { await queryInterface.createTable('users', { id: { type: Sequelize.UUID, defaultValue: Sequelize.UUIDV4, primaryKey: true }, username: { type: Sequelize.STRING(50), unique: true, allowNull: false }, email: { type: Sequelize.STRING(255), unique: true, allowNull: false }, password: { type: Sequelize.STRING(255), allowNull: false }, role: { type: Sequelize.ENUM('user', 'admin'), defaultValue: 'user' }, profile_image: { type: Sequelize.STRING(500) }, created_at: { type: Sequelize.DATE, defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') }, updated_at: { type: Sequelize.DATE, defaultValue: Sequelize.literal('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP') }, last_login: { type: Sequelize.DATE, allowNull: true }, total_quizzes: { type: Sequelize.INTEGER, defaultValue: 0 }, total_questions: { type: Sequelize.INTEGER, defaultValue: 0 }, correct_answers: { type: Sequelize.INTEGER, defaultValue: 0 }, streak: { type: Sequelize.INTEGER, defaultValue: 0 } }); // Add indexes await queryInterface.addIndex('users', ['email']); await queryInterface.addIndex('users', ['username']); await queryInterface.addIndex('users', ['role']); }, down: async (queryInterface, Sequelize) => { await queryInterface.dropTable('users'); } }; ``` --- ## Migration 2: Create Categories Table **File**: `migrations/20250101000002-create-categories.js` ```javascript 'use strict'; module.exports = { up: async (queryInterface, Sequelize) => { await queryInterface.createTable('categories', { id: { type: Sequelize.UUID, defaultValue: Sequelize.UUIDV4, primaryKey: true }, name: { type: Sequelize.STRING(100), unique: true, allowNull: false }, description: { type: Sequelize.TEXT }, icon: { type: Sequelize.STRING(255) }, slug: { type: Sequelize.STRING(100), unique: true, allowNull: false }, question_count: { type: Sequelize.INTEGER, defaultValue: 0 }, is_active: { type: Sequelize.BOOLEAN, defaultValue: true }, guest_accessible: { type: Sequelize.BOOLEAN, defaultValue: false }, public_question_count: { type: Sequelize.INTEGER, defaultValue: 0 }, registered_question_count: { type: Sequelize.INTEGER, defaultValue: 0 }, created_at: { type: Sequelize.DATE, defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') }, updated_at: { type: Sequelize.DATE, defaultValue: Sequelize.literal('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP') } }); // Add indexes await queryInterface.addIndex('categories', ['slug']); await queryInterface.addIndex('categories', ['is_active']); await queryInterface.addIndex('categories', ['guest_accessible']); }, down: async (queryInterface, Sequelize) => { await queryInterface.dropTable('categories'); } }; ``` --- ## Migration 3: Create Questions Table **File**: `migrations/20250101000003-create-questions.js` ```javascript 'use strict'; module.exports = { up: async (queryInterface, Sequelize) => { await queryInterface.createTable('questions', { id: { type: Sequelize.UUID, defaultValue: Sequelize.UUIDV4, primaryKey: true }, question: { type: Sequelize.TEXT, allowNull: false }, type: { type: Sequelize.ENUM('multiple', 'trueFalse', 'written'), allowNull: false }, category_id: { type: Sequelize.UUID, allowNull: false, references: { model: 'categories', key: 'id' }, onUpdate: 'CASCADE', onDelete: 'RESTRICT' }, difficulty: { type: Sequelize.ENUM('easy', 'medium', 'hard'), allowNull: false }, options: { type: Sequelize.JSON, comment: 'Array of answer options for multiple choice questions' }, correct_answer: { type: Sequelize.STRING(500) }, explanation: { type: Sequelize.TEXT, allowNull: false }, keywords: { type: Sequelize.JSON, comment: 'Array of keywords for search' }, tags: { type: Sequelize.JSON, comment: 'Array of tags' }, visibility: { type: Sequelize.ENUM('public', 'registered', 'premium'), defaultValue: 'registered' }, is_guest_accessible: { type: Sequelize.BOOLEAN, defaultValue: false }, created_by: { type: Sequelize.UUID, references: { model: 'users', key: 'id' }, onUpdate: 'CASCADE', onDelete: 'SET NULL' }, created_at: { type: Sequelize.DATE, defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') }, updated_at: { type: Sequelize.DATE, defaultValue: Sequelize.literal('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP') }, is_active: { type: Sequelize.BOOLEAN, defaultValue: true }, times_attempted: { type: Sequelize.INTEGER, defaultValue: 0 }, correct_rate: { type: Sequelize.DECIMAL(5, 2), defaultValue: 0.00 } }); // Add indexes await queryInterface.addIndex('questions', ['category_id']); await queryInterface.addIndex('questions', ['difficulty']); await queryInterface.addIndex('questions', ['type']); await queryInterface.addIndex('questions', ['visibility']); await queryInterface.addIndex('questions', ['is_active']); await queryInterface.addIndex('questions', ['is_guest_accessible']); // Add full-text index await queryInterface.sequelize.query( 'CREATE FULLTEXT INDEX idx_questions_fulltext ON questions(question, explanation)' ); }, down: async (queryInterface, Sequelize) => { await queryInterface.dropTable('questions'); } }; ``` --- ## Migration 4: Create Guest Sessions Table **File**: `migrations/20250101000004-create-guest-sessions.js` ```javascript 'use strict'; module.exports = { up: async (queryInterface, Sequelize) => { await queryInterface.createTable('guest_sessions', { id: { type: Sequelize.UUID, defaultValue: Sequelize.UUIDV4, primaryKey: true }, guest_id: { type: Sequelize.STRING(100), unique: true, allowNull: false }, device_id: { type: Sequelize.STRING(255), allowNull: false }, session_token: { type: Sequelize.STRING(500), allowNull: false }, quizzes_attempted: { type: Sequelize.INTEGER, defaultValue: 0 }, max_quizzes: { type: Sequelize.INTEGER, defaultValue: 3 }, created_at: { type: Sequelize.DATE, defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') }, expires_at: { type: Sequelize.DATE, allowNull: false }, ip_address: { type: Sequelize.STRING(45) }, user_agent: { type: Sequelize.TEXT } }); // Add indexes await queryInterface.addIndex('guest_sessions', ['guest_id']); await queryInterface.addIndex('guest_sessions', ['session_token']); await queryInterface.addIndex('guest_sessions', ['expires_at']); }, down: async (queryInterface, Sequelize) => { await queryInterface.dropTable('guest_sessions'); } }; ``` --- ## Migration 5: Create Quiz Sessions Table **File**: `migrations/20250101000005-create-quiz-sessions.js` ```javascript 'use strict'; module.exports = { up: async (queryInterface, Sequelize) => { await queryInterface.createTable('quiz_sessions', { id: { type: Sequelize.UUID, defaultValue: Sequelize.UUIDV4, primaryKey: true }, user_id: { type: Sequelize.UUID, references: { model: 'users', key: 'id' }, onUpdate: 'CASCADE', onDelete: 'CASCADE' }, guest_session_id: { type: Sequelize.UUID, references: { model: 'guest_sessions', key: 'id' }, onUpdate: 'CASCADE', onDelete: 'CASCADE' }, is_guest_session: { type: Sequelize.BOOLEAN, defaultValue: false }, category_id: { type: Sequelize.UUID, references: { model: 'categories', key: 'id' }, onUpdate: 'CASCADE', onDelete: 'SET NULL' }, start_time: { type: Sequelize.DATE, defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') }, end_time: { type: Sequelize.DATE, allowNull: true }, score: { type: Sequelize.INTEGER, defaultValue: 0 }, total_questions: { type: Sequelize.INTEGER, allowNull: false }, status: { type: Sequelize.ENUM('in-progress', 'completed', 'abandoned'), defaultValue: 'in-progress' }, completed_at: { type: Sequelize.DATE, allowNull: true }, created_at: { type: Sequelize.DATE, defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') }, updated_at: { type: Sequelize.DATE, defaultValue: Sequelize.literal('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP') } }); // Add indexes await queryInterface.addIndex('quiz_sessions', ['user_id']); await queryInterface.addIndex('quiz_sessions', ['guest_session_id']); await queryInterface.addIndex('quiz_sessions', ['status']); await queryInterface.addIndex('quiz_sessions', ['completed_at']); }, down: async (queryInterface, Sequelize) => { await queryInterface.dropTable('quiz_sessions'); } }; ``` --- ## Seeder Example: Demo Categories **File**: `seeders/20250101000001-demo-categories.js` ```javascript 'use strict'; const { v4: uuidv4 } = require('uuid'); module.exports = { up: async (queryInterface, Sequelize) => { const categories = [ { id: uuidv4(), name: 'Angular', description: 'Frontend framework by Google', icon: 'angular-icon.svg', slug: 'angular', question_count: 0, is_active: true, guest_accessible: true, public_question_count: 0, registered_question_count: 0, created_at: new Date(), updated_at: new Date() }, { id: uuidv4(), name: 'Node.js', description: 'JavaScript runtime for backend', icon: 'nodejs-icon.svg', slug: 'nodejs', question_count: 0, is_active: true, guest_accessible: true, public_question_count: 0, registered_question_count: 0, created_at: new Date(), updated_at: new Date() }, { id: uuidv4(), name: 'MySQL', description: 'Relational database management system', icon: 'mysql-icon.svg', slug: 'mysql', question_count: 0, is_active: true, guest_accessible: false, public_question_count: 0, registered_question_count: 0, created_at: new Date(), updated_at: new Date() }, { id: uuidv4(), name: 'Express.js', description: 'Web framework for Node.js', icon: 'express-icon.svg', slug: 'expressjs', question_count: 0, is_active: true, guest_accessible: true, public_question_count: 0, registered_question_count: 0, created_at: new Date(), updated_at: new Date() }, { id: uuidv4(), name: 'JavaScript', description: 'Core programming language', icon: 'javascript-icon.svg', slug: 'javascript', question_count: 0, is_active: true, guest_accessible: true, public_question_count: 0, registered_question_count: 0, created_at: new Date(), updated_at: new Date() } ]; await queryInterface.bulkInsert('categories', categories); }, down: async (queryInterface, Sequelize) => { await queryInterface.bulkDelete('categories', null, {}); } }; ``` --- ## Seeder Example: Demo Admin User **File**: `seeders/20250101000002-demo-admin.js` ```javascript 'use strict'; const bcrypt = require('bcrypt'); const { v4: uuidv4 } = require('uuid'); module.exports = { up: async (queryInterface, Sequelize) => { const hashedPassword = await bcrypt.hash('Admin@123', 10); await queryInterface.bulkInsert('users', [{ id: uuidv4(), username: 'admin', email: 'admin@quizapp.com', password: hashedPassword, role: 'admin', profile_image: null, created_at: new Date(), updated_at: new Date(), last_login: null, total_quizzes: 0, total_questions: 0, correct_answers: 0, streak: 0 }]); }, down: async (queryInterface, Sequelize) => { await queryInterface.bulkDelete('users', { email: 'admin@quizapp.com' }, {}); } }; ``` --- ## Running Migrations ```bash # Create database first mysql -u root -p CREATE DATABASE interview_quiz_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; EXIT; # Run all migrations npx sequelize-cli db:migrate # Check migration status npx sequelize-cli db:migrate:status # Undo last migration npx sequelize-cli db:migrate:undo # Undo all migrations npx sequelize-cli db:migrate:undo:all # Run seeders npx sequelize-cli db:seed:all # Undo seeders npx sequelize-cli db:seed:undo:all ``` --- ## .sequelizerc Configuration **File**: `.sequelizerc` (in project root) ```javascript 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 Configuration **File**: `config/database.js` ```javascript require('dotenv').config(); module.exports = { development: { username: process.env.DB_USER || 'root', password: process.env.DB_PASSWORD || '', database: process.env.DB_NAME || 'interview_quiz_db', host: process.env.DB_HOST || 'localhost', port: process.env.DB_PORT || 3306, dialect: 'mysql', logging: console.log, pool: { max: 10, min: 0, acquire: 30000, idle: 10000 } }, test: { username: process.env.DB_USER || 'root', password: process.env.DB_PASSWORD || '', database: 'interview_quiz_test', host: process.env.DB_HOST || 'localhost', port: process.env.DB_PORT || 3306, dialect: 'mysql', logging: false }, production: { username: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, host: process.env.DB_HOST, port: process.env.DB_PORT || 3306, dialect: 'mysql', logging: false, pool: { max: 20, min: 5, acquire: 30000, idle: 10000 }, dialectOptions: { ssl: { require: true, rejectUnauthorized: false } } } }; ``` --- That's it! Your migration files are ready to use. 🚀