sequelize beforeCreate hook is not executing - node.js

I wrote a BeforeCreate hook in my sequelize model. when i hit create user route then it saying user.user_id can't be null and even before create hook function not executing. I have followed documentation of sequelize.They have mentioned same as I use.I wrote a BeforeCreate hook in my sequelize model. when i hit create user route then it saying user.user_id can't be null and even before create hook function not executing. I have followed documentation of sequelize.They have mentioned same as I use.
const sequelize = require("kvell-db-plugin-sequelize").dbInstance;
const Sequelize = require("kvell-db-plugin-sequelize").dbLib;
const shortid = require("shortid");
const User = sequelize.define(
"user",
{
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
allowNull: false
},
user_id: {
type: Sequelize.STRING,
allowNull: false,
primaryKey: true,
unique: true
},
user_fname: {
type: Sequelize.STRING,
allowNull: false
},
user_lname: {
type: Sequelize.STRING,
allowNull: false
},
user_fullName: {
type: Sequelize.VIRTUAL,
get() {
return `${this.user_fname} ${this.user_lname}`;
},
set(value) {
throw new Error("Do not try to set the `fullName` value!");
}
},
user_email: {
type: Sequelize.STRING,
allowNull: false,
primaryKey: true,
validate: {
isEmail: true
},
unique: {
args: true,
msg: "Email address already in use!"
}
},
user_credential: {
type: Sequelize.STRING,
allowNull: false
},
user_roles: {
type: Sequelize.ARRAY(Sequelize.STRING),
allowNull: false,
defaultValue: ["Admin"]
},
admin: {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: true
},
user_img: {
type: Sequelize.STRING,
allowNull: true
}
},
{
timestamps: true
}
);
User.beforeCreate(async (user, options) => {
console.log("inside hooks");
let id = `user_${shortid.generate()}`;
user.user_id = id;
});
const toJSON = User.prototype.toJSON;
User.prototype.toJSON = function({ attributes = [] } = {}) {
const obj = toJSON.call(this);
if (!attributes.length) {
return obj;
}
return attributes.reduce((result, attribute) => {
result[attribute] = obj[attribute];
return result;
}, {});
};
module.exports = User;

The real answer (alluded to, but not explicitly stated, in one of the comments) is that beforeCreate hooks are applied after model validation.
This means if you have any field in your model (eg id ) which cannot be null, Sequelize will evaluate this prior to applying the beforeCreate field. In your case, Sequelize never gets as far as the beforeCreate hook, because the null id is failing validation every time.
Your accepted answer works around this by setting allowNull = true, thus circumventing the validation of your (briefly) null id. But the better option (rather than to distort your model by allowing null id) is almost certainly to instead use the correct hook: beforeValidate. This hook is applied before the model criteria are evaluated.
It is a very simple change:
User.beforeValidate(async (user, options) => {
console.log("inside hooks");
let id = `user_${shortid.generate()}`;
user.user_id = id;
});
NB: async is redundant here.

user_id: {
type: Sequelize.STRING,
allowNull: false,
primaryKey: true,
unique: true
}
In this section I have set user_id allowNull = false beforeCreate execute after the beforeValidate hook. But in beforeValidate hooks it was throwing error cause of user.user_id===null so i allowed allowNull===true and now it's working.

Try to remove async in this piece of your code:
async (user, options)

Related

Sequelize Alter always set all data table foreign key to null

Bug Description
after running sequelize sync with options alter: true, all foreign keys in tables set to null.
await sequelize.sync({ alter: true }).then(async (result) => {
await initDataUserGroup();
await initDataUserAdmin();
await initDataGateType();
});
and here my one model which has foreign key :
const { DataTypes } = require("sequelize");
const { sequelize } = require("../services/DatabaseService");
const UserGroup = require("./UserGroup");
const Biodata = require("./Biodata");
const User = sequelize.define("user", {
username: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
notNull: {
msg: "Username must not be empty",
},
notEmpty: true,
},
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
notNull: true,
notEmpty: true,
isEmail: true,
},
},
password: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notNull: true,
notEmpty: true,
},
},
});
User.UserGroup = User.belongsTo(UserGroup);
User.Biodata = User.hasOne(Biodata, {
foreignKey: "userId",
as: "biodata",
});
module.exports = User;
What do you expect to happen?
It should not set all foreign keys to null after running sync with options { alter: true }
What is actually happening?
userGroupId in table user is null and userId in table biodata is also null.
Environment
Sequelize version: ^6.6.5
Node.js version: 12.9.1
Dialect: SQLite (SQLite3)
From my issue : https://github.com/sequelize/sequelize/issues/13464
anyone can help me please?
after struggling for a couple of days, I managed to resolve it by disabling and re-enabling foreign keys before and after syncing :
await sequelize.query("PRAGMA foreign_keys = false;");
await sequelize
.sync({ alter: true })
.then(async (result) => {
await initDataUserGroup();
await initDataGateType();
})
.catch((errorMessage) => console.error("sync error = ", errorMessage));
await sequelize.query("PRAGMA foreign_keys = true;");
Feel free to inform me a better solution than the current one.

How to access the data from mapping table in graphql using sequelize

i am new to sequelize, i have a user table , address table and address type table as given below.
A user can have 2 a different address , permanent and current address, and the type of address (permanent or current ) is specified in the table address type.
I have tried to access the data from mapping table (address_type) in the resolver based on schema and set hasMany relation from user -> address table , but graphql shows association not found error.
How can we get the relation properly in order to get the mapping address type name.
type User{
id:Int
name:String
}
type Address {
id: ID!
user_id:Int
city: String
addr_type:AddressType
}
type AddressType{
id : Int
name:String (permanent|current)
}
table definition
module.exports = function(sequelize, DataTypes) {
return sequelize.define('user', {
id: {
type: DataTypes.INTEGER, allowNull: false, primaryKey: true, autoIncrement: true
},
name: {
type: DataTypes.INTEGER, allowNull: false,
},
}, {
tableName: 'user',
timestamps: false
});
};
module.exports = function(sequelize, DataTypes) {
return sequelize.define('address', {
id: {
type: DataTypes.INTEGER, allowNull: false, primaryKey: true, autoIncrement: true
},
user_id: {
type: DataTypes.INTEGER, allowNull: false, field:"addr_type"
},
addr_type: {
type: DataTypes.INTEGER, allowNull: false, field:"addr_type"
},
city: {
type: DataTypes.STRING, allowNull: false,
},
}, {
tableName: 'address',
timestamps: false
});
};
module.exports = function(sequelize, DataTypes) {
return sequelize.define('address_types', {
id: {
type: DataTypes.INTEGER, allowNull: false, primaryKey: true, autoIncrement: true
},
name: {
type: DataTypes.STRING, allowNull: false,
},
}, {
tableName: 'address_type',
timestamps: false
});
};
relationship
db.user.hasMany(db.address,{foreignKey: 'user_id'});
db.address.belongsTo(db.user,{foreignKey: 'user_id'});
db.address.belongsTo(db.address_types,{foreignKey: 'addr_type'});
resolver code
userts: async (obj, args, context, info ) => User.findAll( {
where: { user_status: 1 },
,
raw: true,
nest: true,
} ).then(userts => {
const response = userts.map(usert => {
return{
// i have 15 fields for a user, if i can access the schema of the corresponsing resolver i can dynamically build the response out put
id: usert.id,
firstName: usert.firstName,
lastName: usert.lastName,
middleName: usert.middleName,
}
})
return response;
}),
You should turn off the option raw in order to get associated objects and use the include option to indicate what associated models you wish to load.
User.findAll( {
where: { user_status: 1 },
include: [{
model: Address,
include: AddressType
}],
raw: false,
nest: true,
}

How do I add a composite foreign key in sequelize?

I have defined the following tables, archive and infos. Each file can have many information tags. The combination of field and tag is unique. For this I require a composite primary key consisting of field and tag. field refers to field in the archive table. The model definitions are given below.
Archive table:
module.exports = (sequelize, DataTypes) => {
let archive = sequelize.define('archive', {
fileid: {
type: DataTypes.STRING,
primaryKey: true,
allowNull: false
},
filename: {
type: DataTypes.STRING,
unique: false,
allowNull: false
},
originalname: {
type: DataTypes.STRING,
unique: false,
allowNull: false
},
downloadlink: {
type: DataTypes.STRING,
unique: false,
allowNull: false
},
domain: {
type: DataTypes.STRING,
unique: false,
allowNull: false
},
sem: {
type: DataTypes.INTEGER,
unique: false,
allowNull: false
},
branch: {
type: DataTypes.STRING,
unique: false,
allowNull: false
}
});
archive.associate = models => {
models.archive.hasMany(models.info, {
foreignKey: 'fileid'
});
models.archive.hasMany(models.upvotes, {
foreignKey: 'fileid'
});
};
return archive;
};
Info Table
module.exports = (sequelize, DataTypes) => {
let info = sequelize.define('info', {
tag: {
type: DataTypes.STRING,
allowNull: false,
primaryKey: true
}
});
info.associate = models => {
models.info.belongsTo(models.archive, {
foreignKey: 'fileid',
primaryKey: true
});
};
return info;
};
Making the primaryKey: true did not work. I have tried through: as well. I cannot seem to make it work.
Sequelize can be a giant pain. I follow the docs to the letter and still things don't work with some of the more complex queries, such as can be the case with composite primary key. My suggestion to you is when you bump in to Model fails, go with Raw queries. That being said still take the extra steps to protect against SQL injection attacks.
here is an example:
ensure that you include type
santize variables (in this case code)
db.sequelize
.query('SELECT count(*) FROM logs WHERE code = :code ', {
replacements: {
code: code
},
type: Sequelize.QueryTypes.SELECT
})
.then((data) => {
...
})
.catch((err) => {
...
});
I would have posted this a comment but there was not enough space.

Node.JS: Sequelize association set fails to restore record on paranoid

I have 2 models users and tags, both are associated through another model called usersTags and all 3 models have paranoid set with custom timestamps. I understand that associating models will add additional methods to work on the associations to all associated models, so i am wanting to making a simple setTags call for users, the docs shows that if in the array in the method does not contain the element that is stored in the database it should be removed, otherwise it should be created/restored.
So i try to restore a previously removed tag but for some reason it fails. The models are defined as following:
Users
module.exports = function(sequelize, DataTypes) {
const Users = sequelize.define("users", {
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true
},
username: {
type: DataTypes.STRING(100),
allowNull: false,
validate: {
len: {
args: [3, 100],
msg: "String length is not in this range"
}
}
},
password: {
type: DataTypes.STRING(100),
allowNull: false,
field: "password_hash"
}
}, {
tableName: "users",
createdAt: "create_time",
updatedAt: "update_time",
deletedAt: "delete_time",
paranoid: true
});
Users.associate = function(models) {
// Add this association to include tag records
this.belongsToMany(models.tags, {
through: {
model: models.usersTags,
unique: true
},
foreignKey: "users_id",
constraints: false
});
};
return Users;
};
Tags
module.exports = function(sequelize, DataTypes) {
const Tags = sequelize.define("tags", {
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING(45),
allowNull: false
}
}, {
tableName: "tags",
createdAt: "create_time",
updatedAt: "update_time",
deletedAt: "delete_time",
paranoid: true
});
Tags.associate = function(models) {
this.belongsToMany(models.users, {
through: {
model: models.usersTags,
unique: true
},
foreignKey: "tags_id",
constraints: false
});
};
return Tags;
};
usersTags
module.exports = function(sequelize, DataTypes) {
const UsersTags = sequelize.define("usersTags", {
users_id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
references: {
model: "users",
key: "id"
}
},
tags_id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
references: {
model: "tags",
key: "id"
}
}
}, {
tableName: "users_tags",
createdAt: "create_time",
updatedAt: "update_time",
deletedAt: "delete_time",
paranoid: true,
indexes: [
{
unique: true,
fields: ["users_id", "tags_id"]
}
]
});
return UsersTags;
};
Test
let _user;
models.users.findOne({where: {id: 100}})
.then(user => {
_user = user;
return _user.setTags([1]); // Successfully create association tag with id 1
})
.then(() => _user.setTags([])) // Successfully remove all associated tags
.then(() => _user.setTags([1])); // Should restore association tag with id 1 but fails
Executed query
app:database Executing (default): SELECT `id`, `username`, `first_name`, `last_name`, `birthday`, `description`, `location`, `email`, `type`, `image_path` FROM `users` AS `users` WHERE ((`users`.`delete_time` > '2018-08-28 19:40:15' OR `users`.`delete_time` IS NULL) AND `users`.`id` = 100); +0ms
app:database Executing (default): SELECT `users_id`, `tags_id`, `create_time`, `update_time`, `delete_time` FROM `users_tags` AS `usersTags` WHERE ((`usersTags`.`delete_time` > '2018-08-28 19:40:15' OR `usersTags`.`delete_time` IS NULL) AND `usersTags`.`users_id` = 100); +6ms
app:database Executing (default): INSERT INTO `users_tags` (`users_id`,`tags_id`,`create_time`,`update_time`) VALUES (100,1,'2018-08-28 19:40:15','2018-08-28 19:40:15'); +7ms
For some reason the tag search query is failing to retrieve the tag that contains the delete_time set and therefore the last query is insert instead of update, i know the workaround would be to set paranoid to false but i have to keep track of all activities, i know another workaround would be to create a custom model method to handle this but i still want to know if there is a way to achieve this without having to create an additional custom method
your code in not in a correct async order so your _user global variable is not initiated,I think this is the correct order :
let _user;
models.users.findOne({where: {id: 100}})
.then(user => {
_user = user;
_user.setTags([]).then(()=>{
_user.setTags([1])
})
})

Node js and Sequelize insert to database

In my project I am using MySQL database and Sequelize Js,
I have two models created:
Post code model:
module.exports = function(sequelize, Sequelize) {
var Post_code = sequelize.define('post_code', {
id: {
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER(11)
},
code: {
type: Sequelize.STRING(16),
allowNull: false,
unique: true
},
city: {
type: Sequelize.STRING(45),
allowNull: false
}
},{
freezeTableName: true,
underscored: true
});
Post_code.associate = function(models) {
models.post_code.hasMany(models.building);
};
return Post_code;
}
Building model:
module.exports = function(sequelize, Sequelize) {
var Building = sequelize.define('building', {
id: {
autoIncrement: true,
primaryKey: true,
allowNull: false,
type: Sequelize.INTEGER(11)
},
name: {
type: Sequelize.STRING(100),
allowNull: false
},
number: {
type: Sequelize.INTEGER(11),
allowNull: false
},
address: {
type: Sequelize.STRING(255),
allowNull: false
},
latitude: {
type: Sequelize.DECIMAL(9,6),
allowNull: false
},
longitude: {
type: Sequelize.DECIMAL(9,6),
allowNull: false
}
},{
freezeTableName: true,
underscored: true
});
Building.associate = function (models) {
models.building.belongsTo(models.post_code, {
foreignKey: {
allowNull: false
}
});
models.building.hasMany(models.flat);
};
return Building;
};
They are in relation one to many, it follows that Post code has many Buildings:
I want to add new building to database, when POST request is send to this route:
"/post-codes/:post_code_id/buildings"
I have access to post_code_id but I don't know how to correctly associate post_code model with building.
I was trying to do something like this:
models.post_code.findById(req.params.post_code_id)
.then(function(postCode){
postCode.setBuilding(
models.building.create({
}).then(function(building){});
);
});
But without result. I would be grateful if someone could explain how to make inserts correctly.
I haven't heard about the include option on create, but you can do something like this:
db.post_code.find({
where: {
id: req.params.post_code_id
}
}).then(post_code => {
if (!post_code) {
return res.status(404).send({
message: 'No post_code with that identifier has been found'
});
} else {
//create here your building
}
}).catch(err => {
return res.jsonp(err)
});
I don't know if this is the best way to do it, but it verify first if the post_code exists.

Resources