Sequelize Associations and Relationships: Complete Guide
Sequelize associations define relationships between models, enabling you to work with related data efficiently. In this guide, we'll learn how to define and use Sequelize associations (hasMany, belongsTo, hasOne) for product-category and order-item relationships.
Sequelize associations define relationships between models, enabling you to work with related data efficiently. In this guide, we'll learn how to define and use Sequelize associations (hasMany, belongsTo, hasOne) for product-category and order-item relationships.
Defining Models
Setting up Category and Product models:
const { DataTypes } = require("sequelize");
const database = require("./database");
// Category Model
const Category = database.getSequelize().define("Category", {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
description: {
type: DataTypes.TEXT,
allowNull: true,
},
}, {
tableName: "categories",
timestamps: true,
});
// Product Model
const Product = database.getSequelize().define("Product", {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
categoryId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: Category,
key: "id",
},
},
price: {
type: DataTypes.DECIMAL(10, 2),
allowNull: false,
},
}, {
tableName: "products",
timestamps: true,
});Defining Associations
Setting up associations between models:
// One-to-Many: Category has many Products
Category.hasMany(Product, {
foreignKey: "categoryId",
as: "products", // Alias for accessing
});
// Many-to-One: Product belongs to Category
Product.belongsTo(Category, {
foreignKey: "categoryId",
as: "category", // Alias for accessing
});
// Export models
module.exports = { Category, Product };Using Associations in Queries
Querying with includes (eager loading):
// Get all products with their categories
const products = await Product.findAll({
include: [{
model: Category,
as: "category",
attributes: ["id", "name"], // Only get specific fields
}],
});
// Transform to include categoryName at root level
const transformedProducts = products.map(product => {
const data = product.toJSON();
return {
...data,
categoryName: data.category ? data.category.name : "Unknown",
};
});
// Get category with all products
const category = await Category.findByPk(categoryId, {
include: [{
model: Product,
as: "products",
attributes: ["id", "name", "price"],
}],
});
// Access related data
console.log(category.products); // Array of productsMany-to-Many Relationships
Setting up many-to-many relationships:
// Order and Product (many-to-many through OrderItem)
const Order = database.getSequelize().define("Order", {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
orderNumber: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
total: {
type: DataTypes.DECIMAL(10, 2),
allowNull: false,
},
});
const OrderItem = database.getSequelize().define("OrderItem", {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
orderId: {
type: DataTypes.INTEGER,
references: { model: Order, key: "id" },
},
productId: {
type: DataTypes.INTEGER,
references: { model: Product, key: "id" },
},
quantity: {
type: DataTypes.INTEGER,
allowNull: false,
},
price: {
type: DataTypes.DECIMAL(10, 2),
allowNull: false,
},
});
// Associations
Order.hasMany(OrderItem, { foreignKey: "orderId", as: "items" });
OrderItem.belongsTo(Order, { foreignKey: "orderId", as: "order" });
OrderItem.belongsTo(Product, { foreignKey: "productId", as: "product" });
Product.hasMany(OrderItem, { foreignKey: "productId", as: "orderItems" });Querying with Nested Includes
// Get order with items and products
const order = await Order.findByPk(orderId, {
include: [{
model: OrderItem,
as: "items",
include: [{
model: Product,
as: "product",
attributes: ["id", "name", "price"],
}],
}],
});
// Access nested data
order.items.forEach(item => {
console.log(item.product.name); // Product name
console.log(item.quantity); // Quantity
console.log(item.price); // Item price
});Best Practices
- Always define associations in both directions (hasMany and belongsTo)
- Use aliases for clearer code (as: "category", as: "products")
- Specify attributes in includes to avoid fetching unnecessary data
- Use eager loading (includes) to avoid N+1 queries
- Define foreign keys explicitly in models
- Use proper cascade options for deletions
Conclusion
Sequelize associations provide a powerful way to work with related data in Node.js applications. With proper associations, you can efficiently query related data, avoid N+1 problems, and maintain referential integrity. This is essential for inventory management systems with complex relationships between products, categories, orders, and items.