I have two models Category and Product. The product can have one Category, Category can have many products. This is a One-to-Many relationship.
product.js file:
import { DataTypes, Model } from 'sequelize';
import connection from '../connection';
import Category from '@/database/models/category';
const initProduct = (sequelize, Types) => {
class Product extends Model {
}
Product.init(
{
name: Types.STRING,
description: Types.STRING,
price: Types.NUMBER,
order: Types.NUMBER,
categoryId: Types.INTEGER,
showOnMenu: Types.BOOLEAN
},
{
sequelize,
modelName: 'Product',
tableName: 'Product',
createdAt: 'created_at',
updatedAt: 'updated_at'
}
);
Product.associate = () => {
Product.belongsTo(Category);
};
return Product;
};
export default initProduct(connection, DataTypes);
migration-product.js
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Product', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
description: {
type: Sequelize.STRING
},
categoryId: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'Category',
key: 'id'
}
},
price: {
type: Sequelize.NUMERIC
},
order: {
type: Sequelize.INTEGER
},
showOnMenu: {
type: Sequelize.BOOLEAN
},
created_at: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
},
updated_at: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
}
});
},
async down(queryInterface) {
await queryInterface.dropTable('Product');
}
};
category.js
import { DataTypes, Model } from 'sequelize';
import connection from '../connection';
import Product from '@/database/models/product';
const initCategory = (sequelize, Types) => {
class Category extends Model {
}
Category.init(
{
name: Types.STRING,
description: Types.STRING,
order: Types.NUMBER,
showOnMenu: Types.BOOLEAN
},
{
sequelize,
modelName: 'Category',
tableName: 'Category',
createdAt: 'created_at',
updatedAt: 'updated_at'
}
);
Category.associate = () => {
Category.hasMany(Product);
};
return Category;
};
export default initCategory(connection, DataTypes);
migration-category.js
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Category', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
description: {
type: Sequelize.STRING
},
order: {
type: Sequelize.INTEGER
},
showOnMenu: {
type: Sequelize.BOOLEAN
},
created_at: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
},
updated_at: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
}
});
},
async down(queryInterface) {
await queryInterface.dropTable('Category');
}
};
Here I am connecting with Sequelize
connection.js
import Sequelize from 'sequelize';
import config from './config/config.mjs';
let sequelize;
if (process.env.NODE_ENV === 'production') {
sequelize = new Sequelize(config.production);
} else if (process.env.NODE_ENV === 'staging') {
sequelize = new Sequelize(config.staging);
} else if (process.env.NODE_ENV === 'test') {
sequelize = new Sequelize(config.test);
} else {
sequelize = new Sequelize(config.development);
}
const connection = sequelize;
export default connection;
I'm trying to get all the products and include category name: In SQL it will be like this:
SELECT p.id, p.name, p.price, c.name AS categoryName FROM Product p
INNER JOIN Category c ON p.categoryId = c.id;
In sequelize I'm trying like this:
const products = await Product.findAll({
include: {
model: Category,
attributes: ['id', 'name']
},
attributes: ['id', 'name', 'price']
});
I'm getting this error
EagerLoadingError [SequelizeEagerLoadingError]: Category is not associated to Product!
I'm not sure how I can solve this error. The tables are created OK, I can see FK in the Product table
Product_ibfk_1 -> (categoryId) -> Category (id)