Models: Your Application's View of Data
Think of a model as an architect's blueprint of a building. Just as a blueprint defines the structure and rules for a building, a model defines the structure and rules for your data. Let's explore how models work in practice:
Basic Model Definition
// models/user.js
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
firstName: {
type: DataTypes.STRING,
allowNull: false
},
email: {
type: DataTypes.STRING,
unique: true,
validate: {
isEmail: true
}
},
status: {
type: DataTypes.ENUM('active', 'inactive'),
defaultValue: 'active'
}
});
// Define relationships
User.associate = (models) => {
User.hasMany(models.Post, {
foreignKey: 'userId',
as: 'posts'
});
};
return User;
};
Models vs Migrations: Understanding the Difference
Imagine you're writing a book. The model is like your outline - it describes what the book should contain. Migrations are like the editing process - they represent the actual changes you make to transform your draft into the final book.
Models
Models are like living blueprints that your application uses to interact with data:
- Define the current structure of your data
- Handle validation and business logic
- Manage relationships between different types of data
- Provide an interface for your application code
Migrations
Migrations are like a historical record of how your database has evolved:
- Record specific changes to your database structure
- Allow for version control of your database schema
- Enable collaboration between team members
- Provide a way to roll back changes if needed
Working with Production Databases
Updating a production database is like performing surgery - it requires careful planning and precise execution. Here's how migrations help:
Adding a New Column
// migrations/YYYYMMDDHHMMSS-add-phone-to-users.js
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.addColumn('Users', 'phoneNumber', {
type: Sequelize.STRING,
allowNull: true
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.removeColumn('Users', 'phoneNumber');
}
};
Real-World Scenario: Building an E-commerce Platform
Let's see how models and migrations work together in a practical scenario:
Initial Product Model
// models/product.js
module.exports = (sequelize, DataTypes) => {
const Product = sequelize.define('Product', {
name: DataTypes.STRING,
price: DataTypes.DECIMAL(10, 2),
stock: DataTypes.INTEGER
});
return Product;
};
// Initial migration
// migrations/YYYYMMDDHHMMSS-create-product.js
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('Products', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
price: {
type: Sequelize.DECIMAL(10, 2)
},
stock: {
type: Sequelize.INTEGER
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Products');
}
};
Adding Product Categories Later
// migrations/YYYYMMDDHHMMSS-add-category-to-products.js
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.addColumn('Products', 'categoryId', {
type: Sequelize.INTEGER,
references: {
model: 'Categories',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL'
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.removeColumn('Products', 'categoryId');
}
};
Migration Management
Managing migrations is like maintaining a ship's log - it helps you track where you've been and where you're going:
# View migration history
npx sequelize-cli db:migrate:status
# Roll back all migrations
npx sequelize-cli db:migrate:undo:all
# Roll back to a specific migration
npx sequelize-cli db:migrate:undo --to YYYYMMDDHHMMSS-migration-name.js
Best Practices and Tips
When working with models and migrations, consider these guidelines:
Always Version Control Migrations: Think of migrations as your database's history book - each chapter (migration) tells an important part of the story.
Test Migrations: Before applying migrations to production, test them in a development environment - it's like rehearsing a play before the actual performance.
Backup Before Migrating: Always backup your production database before running migrations - it's your safety net, like having a spare parachute when skydiving.
Keep Migrations Small and Focused: Each migration should do one thing well - like having specialized tools in a toolbox rather than a single multi-purpose tool.