I'm learning node.js and mySQL. I have tried Sequelize and according to my learning source sync should create new table if doesn't exist. But for some reason it doesn't create new table.
Here is my database.js file
const Sequelize = require('sequelize');
const sequelize = new Sequelize('test-schema', 'root', 'mypassword',{dialect:'mysql', host:'localhost'});
module.exports = sequelize;
Here is my model Product file
const Sequelize = require('sequelize');
const sequelize = require('../util/database')
const Product = sequelize.define('product', {
id: {type: Sequelize.INTEGER, autoIncrement: true, allowNull: false, primaryKey: true},
title: Sequelize.STRING,
price: {type: Sequelize.DOUBLE, allowNull: false}
});
module.exports = Product;
and here is my server.js file
const express = require('express');
const sequelize = require('./util/database')
const app = express();
app.get('/',(req,res,next)=>{
res.send({"id": "1"});
});
sequelize.sync().then(result=>{
//console.log(result);
app.listen(3000);
}).catch(err => {
console.log(err);
});
Once I start server I get
Executing (default): SELECT 1+1 AS result
I tried to change the name of my schema in my database file to wrong name and I get an error schema doesn't exist so I believe that connection to db is correct
Here are my installed packages
"dependencies": {
"express": "^4.17.1",
"mysql2": "^2.1.0",
"sequelize": "^5.21.10"
}
Define all models as functions so that you can call them to register models in sequelize and then register models in database.js just like I described in this answer. You can see in the question of this answer how to define a model like a function.
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
id: { type: DataTypes.BIGINT, allowNull: false, autoIncrement: true, unique: true, primaryKey: true },
first_name: DataTypes.STRING,
last_name: DataTypes.STRING
}, {});
User.associate = function(models) {
User.belongsTo(models.Role, { foreignKey: 'role_id' });
};
return User;
};
In your Product file, change that p after sequelize.define from small letter
to capital letter. It should be "Product" not "product". It should look like:
const Product = sequelize.define('Product', {
id: {type: Sequelize.INTEGER, autoIncrement: true, allowNull: false, primaryKey: true},
title: Sequelize.STRING,
price: {type: Sequelize.DOUBLE, allowNull: false}
});
module.exports = Product;
Related
I'm new to sequelize and trying to set it up for my new project. I checked some answers on this, but couldnt get past my error. Can someone point out how to fix this.
models/index.js
// Database service
// Connects to the database
const { Sequelize } = require('sequelize');
const path = require('path');
const sequelize = new Sequelize(process.env.DB_NAME, process.env.DB_USER, process.env.DB_PASS, {
host: process.env.DB_HOST,
dialect: 'mysql',
logging: process.env.QUERY_LOGGING == "true" ? console.log : false,
pool: {
max: 10,
min: 0,
acquire: 30000,
idle: 10000
}
});
module.exports = sequelize
models/users.js
const sequelize = require("./index")
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('Users', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
firstName: {
type: DataTypes.STRING,
allowNull: false
},
lastName: {
type: DataTypes.STRING
},
profileURL: {
type: DataTypes.STRING
},
emailId: {
type: DataTypes.STRING,
allowNull: false
},
passwordHash: {
type: DataTypes.STRING,
allowNull: false
},
street: {
type: DataTypes.STRING
},
city: {
type: DataTypes.STRING,
allowNull: false
},
phone: {
type: DataTypes.STRING
},
newsletter: {
type: DataTypes.STRING
},
visibility: {
type: DataTypes.BOOLEAN,
defaultValue: true
},
},{
});
return User;
};
And finally, I'm importing the User model in my service file like below:
const User = require("../models/users")
const createUser = async(req) => {
const {firstName, lastName, profileURL, emailId, passwordHash, street, city, phone, newsletter, visibility} = req.body
const user = await User.create({
firstName,
lastName,
profileURL,
emailId,
passwordHash,
street,
city,
phone,
newsletter,
visibility
})
console.log("new user==>>", user)
return
}
module.exports = { createUser }
However, I get the following error.
TypeError: User.create is not a function
Can someone point out what I could be doing wrong? I realize it could be something minor.
Thank you
You export a function that registers the User model and not the model itself. So you just need to call it passing sequelize instance and DataTypes somewhere like database.js where you will register all models and their associations or directly in models/index.js:
const UserModelConstructor = require("../models/users")
const { DataTypes } = require("sequelize");
...
const UserModel = UserModelConstructor(sequelize, DataTypes);
module.exports = {
sequelize,
User: UserModel
}
You can look at how to register multiple models and association in my other answer here
Please don't forget to remove this line
const sequelize = require("./index")
from models/users.js
I am making a web application which is similar to 'Twitter'.
When I ran the server,
'user is associated to user multiple times. To identify the correct association,
You must use the 'as' keyword to specify the alias of the association you want to include.'
<- this message appeared.
I applied 'as' keyword for the relationship between 'followers' and 'followings'.
but the error message kept on appearing.
I'm sure that I did something wrong in someway, but I can't figure out what exactly it is.
can somebody help me?
models/index.js
'use strict';
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {};
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}
fs
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(file => {
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
db.User = require('./user')(sequelize, Sequelize);
db.Post = require('./post')(sequelize, Sequelize);
db.Hashtag = require('./hashtag')(sequelize, Sequelize);
db.User.hasMany(db.Post); //1 : n
db.Post.belongsTo(db.User);
// m: n
db.Post.belongsToMany(db.Hashtag, { through: 'PostHashtag' });
db.Hashtag.belongsToMany(db.Post, { through: 'PostHashtag' });
// I use alias here *****
db.User.belongsToMany(db.User, {
foreignKey: 'followingId',
as: 'Followers',
through: 'Follow',
});
db.User.belongsToMany(db.User, {
foreignKey: 'followerId',
as: 'Followings',
through: 'Follow',
});
module.exports = db;
models/user.js
module.exports = (sequelize, DataTypes) => (
sequelize.define('user', {
email: {
type: DataTypes.STRING(40),
allowNull: true,
unique: true,
},
nick: {
type: DataTypes.STRING(15),
allowNull: false,
},
password: {
type: DataTypes.STRING(100),
allowNull: true,
},
provider: {
type: DataTypes.STRING(10),
allowNull: false,
defaultValue: 'local',
},
snsId: {
type: DataTypes.STRING(30),
allowNull: true,
},
}, {
timestamps: true,
paranoid: true,
})
);
models/post.js
module.exports = (sequelize, DataTypes) => (
sequelize.define('user', {
email: {
type: DataTypes.STRING(40),
allowNull: true,
unique: true,
},
nick: {
type: DataTypes.STRING(15),
allowNull: false,
},
password: {
type: DataTypes.STRING(100),
allowNull: true,
},
provider: {
type: DataTypes.STRING(10),
allowNull: false,
defaultValue: 'local',
},
snsId: {
type: DataTypes.STRING(30),
allowNull: true,
},
}, {
timestamps: true,
paranoid: true,
})
);
models/hashtag.js
module.exports = (sequelize, DataTypes) => (
sequelize.define('hashtag', {
title: {
type: DataTypes.STRING(15),
allowNull: false,
unique: true,
},
}, {
timestamps: true,
paranoid: true,
})
);
I think this will solve your problem you have to create a table follower than associate it with user table with following and follower alias and use it to query follower and following
follower Model
module.exports = (sequelize, datatypes) => {
sequelize.define('follower', {
_id: {
type: datatypes.integer,
allownull: false,
primarykey: true,
autoincrement: true
},
userid: {
type: datatypes.integer,
allownull: false
},
followingid: {
type: datatypes.integer,
allownull: false
}
});
}
association
db.follower.belongsTo(db.user, {
as: 'following',
foreignkey: 'followingid'
});
db.follower.belongsto(db.user, {
as: 'follower',
foreignkey: 'userid'
});
query to get follower
follower.findall({
where: {
followingid: userid
},
attributes: ['_id'],
include: [{
model: user, attributes:
['fullname', 'username', '_id', 'picture'],
as: 'follower'
}]
})
I am trying to do a many-to-many assocition with sequelize but I constantly having the same error without reason...
Here is the error I get : SequelizeEagerLoadingError: User is not associated to UserTool!
I searched on the web and my code is corresponding to many right answers that I found.
The M-M association goes like this : User-UserTool-Tool, UserTool is the bridge table between User and Tool
Here is the following code :
Model User - User.js
const Sequelize = require('sequelize');
var db = require('../../config/database');
const User = db.define('User', {
id: {
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
}, {
timestamps: false,
freezeTableName: true,
tableName: 'User'
});
module.exports = User;
Model Tool - Tool.js
const Sequelize = require('sequelize');
var db = require('../../config/database');
const Tool = db.define('Tool', {
id: {
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
}, {
timestamps: false,
freezeTableName: true,
tableName: 'Tool'
});
module.exports = Tool;
Model UserTool - UserTool.js
const Sequelize = require('sequelize');
var db = require('../../config/database');
const UserTool = db.define('UserTool', {
userId: {
type: Sequelize.INTEGER
},
toolId: {
type: Sequelize.INTEGER
},
status: {
type: Sequelize.STRING
},
active: {
type: Sequelize.STRING
},
}, {
timestamps: false,
freezeTableName: true,
tableName: 'UserTool'
});
module.exports = UserTool;
Main.js
const Sequelize = require('sequelize');
var db = require('../../config/database');
const User = require('./User');
const Tool = require('./Tool');
const UserTool = require('./UserTool');
User.belongsToMany(Tool, { through: UserTool });
Tool.belongsToMany(User, { through: UserTool });
module.exports = {
User,
Tool,
UserTool
}
And then when I try to do a findAll() with an include, I get the error mentionned previously.
Here is the code of the findAll with the include
Find.js
const {User, UserTool} = require('./Main');
exports.FindAllUsers = async function (params) {
let message;
await UserTool.findAll({
where: {
userId: params.userId
},
include: [
{
model: User
},
]
});
As I understand it, if you want to query all users. Simply use the following method:
const result = await User.findAll();
If you want to query the user with id = 1 and all tools for that user, using:
const result = await User.findAll({
where: { id: '1' },
include: [{ model: Tool }],
});
sequelize will query the appropriate Tools for User based on the join table UserTool. Eager loading needs both sides(User and Tool) of the model are established associations(belongsToMany). UserTool doesn't establish associations with User, so you can't use include options, that's why sequelize throw that error.
I've got a two MySQL Schemas both with a table called User as I want to have a SSO application with multiple microservices. I can't seem to get Sequelize to generate the correct SQL for my needs.
I've tried adding the 'schema' attribute to my model definitions in Sequelize, but it tries to use 'schema_name.schema_name.table_name' instead of 'schema_name.table_name'. I'm unsure whether the schema attribute works for MySQL.
SuperUser.js
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define(
'User',
{
id: {
primaryKey: true,
type: DataTypes.UUID,
allowNull: false,
},
username: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING,
first_name: DataTypes.STRING,
last_name: DataTypes.STRING,
company: DataTypes.STRING,
},
{
// tried to add schema: super_schema
underscored: true,
timestamps: true,
paranoid: true,
},
);
User.associate = function(models) {};
return User;
};
SubUser.js
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define(
'User',
{
id: {
primaryKey: true,
type: DataTypes.UUID,
allowNull: false,
},
role: {
type: VARCHAR(45),
allowNull: false,
},
},
{
underscored: true,
timestamps: true,
paranoid: true,
},
);
User.associate = function(models) {
const { super } = models;
User.belongsTo(super.models.User, { as: 'Super', foreignKey: 'id' });
};
return User;
};
My query is
const user = await db.sub.User.findOne({
include: [
{
model: db.super.User,
as: 'Super',
where: {
username: 'someUsername',
},
},
],
});
I believe the problem lies in how I'm creating the Sequelize instances, I'm creating an instance for each schema connection. So Sequelize doesn't know that db.super.User is different from db.sub.User when written in Javascript.
The problem lies in the INNER JOIN it generates.
It generates
INNER JOIN `users` AS `Super` ON `User`.`id` = `Super`.`id`
I'd like it to generate
INNER JOIN `Super`.`users` AS `Super` ON `User`.`id` = `Super`.`id`
I'm using Sequelize.js in my Node.js application and keep running into a very strange problem.
Background: I have two models, Account and AccountCategory as follows. My API endpoint calls the route /accounts which calls the accounts controller to do an Account.findAll() query.
Accounts model has a defaultScope to include the related category by default, without having to specify it each time inside the findAll({}) block.
Problem: When the Accounts model is attempting to access and return the data from the database, the defaultScope is trying to include the AccountCategory, Sequelize throws the error:
Include unexpected. Element has to be either a Model, an Association or an object.
I suspect it has to do with the fact that AccountCategory is placed after Account in my models folder when the models are being set up and thus not processed (associated). I base this on the fact that other associations like User and Role (ie. a user has a role) are fine using the same method (ie. no problem with path depth as this answer suggests).
I've spent the last 2 days trying to get the defaultScope working and stop producing this error without any luck. Similar questions do not provide an answer and I would greatly appreciate any help resolving this problem. Thanks.
Account:
module.exports = (sequelize, DataTypes) => {
const Account = sequelize.define(
"Account",
{
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING(100)
},
category_id: {
type: DataTypes.INTEGER(11),
allowNull: false
}
},
{
timestamps: false,
tableName: "Account",
defaultScope: {
include: [{
model: sequelize.models.AccountCategory,
as: "category"
}]
}
}
);
Account.associate = models => {
// Association: Account -> AccountCategory
Account.belongsTo(models.AccountCategory, {
onDelete: "CASCADE",
foreignKey: {
fieldName: "category_id",
allowNull: false,
require: true
},
targetKey: "id",
as: "category"
});
};
return Account;
};
Account Category:
module.exports = (sequelize, DataTypes) => {
var AccountCategory = sequelize.define(
"AccountCategory",
{
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING(30),
allowNull: false,
unique: true
}
},
{
timestamps: false,
tableName: "Account_Category"
}
);
return AccountCategory;
};
Models Index:
const fs = require("fs");
const path = require("path");
const Sequelize = require("sequelize");
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || "development";
const db = {};
const sequelize = new Sequelize(
process.env.DB_NAME,
process.env.DB_USER,
process.env.DB_PASS,
{
host: process.env.DB_HOST,
dialect: "mysql",
operatorAliases: false,
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
}
);
fs.readdirSync(__dirname)
.filter(function(file) {
return (
file.indexOf(".") !== 0 && file !== basename && file.slice(-3) === ".js"
);
})
.forEach(function(file) {
var model = sequelize["import"](path.join(__dirname, file));
db[model.name] = model;
});
Object.keys(db).forEach(function(modelName) {
if (db[modelName].associate) {
db[modelName].associate(db);
}
db[modelName].associate(db);
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
You are correct when you say:
I suspect it has to do with the fact that AccountCategory is placed after Account in my models folder when the models are being set up and thus not processed (associated).
TLDR: Add a new function to your model class definition similar to the associate function, and use the addScope function to define any scopes that reference other models that may have not been initialized due to file tree order. Finally, call that new function the same way you call db[modelName].associate in your models.index.js file.
I had a similar problem and solved it by defining any scopes that reference any models, e.g. in an include, after all the models are initialized after running the following in your models/index.js file.
Here is an example:
models/agent.js
'use strict';
const { Model } = require('sequelize');
const camelCase = require('lodash/camelCase');
const { permissionNames } = require('../../api/constants/permissions');
module.exports = (sequelize, DataTypes) => {
/**
* #summary Agent model
*/
class Agent extends Model {}
Agent.init(
{
id: {
type: DataTypes.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true,
},
firstName: {
type: DataTypes.STRING,
allowNull: false,
},
lastName: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
sequelize,
scopes: {
// Works because the agent-role.js file / model comes before agent.js in the file tree
[camelCase(permissionNames.readAgentRoles)]: {
include: [
{
model: sequelize.models.AgentRole,
},
],
},
// Doesn't work due to import order in models/index.js, i.e., agent.js is before role.js in the file tree
// [camelCase(permissionNames.readRoles)]: {
// include: [
// {
// model: sequelize.models.Role,
// },
// ],
// },
},
}
);
Agent.associate = function (models) {
Agent.belongsToMany(models.Role, {
through: 'AgentRole',
onDelete: 'CASCADE', // default for belongsToMany
onUpdate: 'CASCADE', // default for belongsToMany
foreignKey: {
name: 'agentId',
type: DataTypes.INTEGER,
allowNull: false,
},
});
Agent.hasMany(models.AgentRole, {
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
foreignKey: {
name: 'agentId',
type: DataTypes.INTEGER,
allowNull: false,
},
});
};
// Add a custom `addScopes` function to call after initializing all models in `index.js`
Agent.addScopes = function (models) {
Agent.addScope(camelCase(permissionNames.readRoles), {
include: [
{
model: models.Role,
},
],
});
};
return Agent;
};
models/index.js
'use strict';
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const config = require('../database-config.js');
const db = {};
const sequelize = new Sequelize(config.database, config.username, config.password, config);
/**
* Import and attach all of the model definitions within this 'models' directory to the sequelize instance.
*/
fs.readdirSync(__dirname)
.filter((file) => {
return file.indexOf('.') !== 0 && file !== basename && file.slice(-3) === '.js';
})
.forEach((file) => {
// Here is where file tree order matters... the sequelize const may not have the required model added to it yet
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
db[model.name] = model;
});
Object.keys(db).forEach((modelName) => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
// We need to add scopes that reference other tables once they have all been initialized
if (db[modelName].addScopes) {
db[modelName].addScopes(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
Goodluck!