My app is running nodejs 10.16 and sequelizejs 5.7 with postgresql 11. The initial connection is setup as:
const Sql = require("sequelize");
const db = new Sql('mydb', 'postgres', `${process.env.DB_PASSWORD}`, {
host: 'localhost',
dialect: 'postgres',
port:5432,
} );
A Group model is defined as:
const Joi = require('#hapi/joi');
const Sql = require('sequelize');
const db = require("../startup/db");
const Group = db.define('group', {
name: {type: Sql.STRING,
allowNull: false,
min: 1,
max: 50,
required: true,
},
owner_id: {
type: Sql.INTEGER,
allowNull: false,
required: true,
},
status: {type: Sql.STRING,
allowNull: false,
isIn: ['active', 'inactive']
},
last_updated_by_id: {type: Sql.INTEGER},
createdAt: Sql.DATE,
updatedAt: Sql.DATE
}, {
indexes: [
{
fields: ['name', 'owner_id']
}
]
});
function validateGroup(group) {
const schema = Joi.object({
name: Joi.string()
.min(1)
.max(50)
.required()
.trim(),
status: Joi.string()
.required()
.trim(),
});
return schema.validate(group, {allowUnknown: true});
};
module.exports.Group = Group;
module.exports.validateGroup = validateGroup;
Here is the in router /group/new to save a new group:
router.post('/new', async (req, res) => {
const { err } = validateGroup(req.body);
if (err) return res.status(400).send(err.details[0].message);
let group = Group.build(_.pick(req.body, ['name', 'status']));
group.owner_id = req.user.id;
group.last_updated_by_id = req.user.id;
let grpmember = new Groupmember({owner_id: req.user.id});
try {
await db.transaction(async () => {
await group.save();
grpmember.group_id = group.id;
await grpmember.save();
return res.status(200).send(`${group.name}-已经保存!`);
});
} catch (err) {
console.log("error in saving new group ", err);
return res.status(400).send('保存错误,请再试一遍!');
};
});
Here is the Group table output which contains both empty and non-empty name:
Why the validation for name field, such as allowNull:false and required: true did not validate the input value?
The following is the code that works:
const Group = db.define('group', {
name: {type: Sql.STRING,
validate: {
len:{
args:[1,50],
msg:"Name between 1 to 50 chars"
} ,
}
},
owner_id: {
type: Sql.INTEGER,
validate: {
isInt: true,
},
},
status: {type: Sql.STRING,
validate: {
isIn: ['active', 'inactive'],
},
},
group_data: {
type: Sql.JSONB
},
last_updated_by_id: {type: Sql.INTEGER},
fort_token: {type: Sql.STRING,
validate: {
min:20, //64 for production
},
},
createdAt: Sql.DATE,
updatedAt: Sql.DATE
}
Related
doesn't matter if I try findOne() or findAll()
I get : []
empty array.
the query being called for findAll() is:
SELECT "id", "password", "firstName", "lastName", "email", "phone", "roleId", "userId", "createdAt", "updatedAt" FROM "Users" AS "Users";
while when I manually use in pgadmin cli this query: select * from Users;
I get all the rows of the table.
could you please advise?
const { Sequelize, DataTypes } = require('sequelize');
const sequelize = require('../db/database')
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
//console.log(sequelize);
const User = sequelize.define("Users", {
password: {
type: Sequelize.STRING,
allowNull: false,
validate:{
len: [8,40],
trim: true
}
},
firstName: {
type: Sequelize.STRING,
allowNull: false,
validate:{
len: [0,20],
trim: true,
lowercase: true
}
},
lastName: {
type: Sequelize.STRING,
allowNull: false,
validate:{
len: [0,20],
trim: true,
lowercase: true
}
},
email: {
type: Sequelize.STRING,
unique: true,
allowNull: false,
validate:{
len: [0,20],
trim: true,
lowercase: true,
isEmail: true
}
},
phone: {
type: Sequelize.STRING,
validate:{
len: [10,10],
trim: true,
}
},
roleId: {
type: Sequelize.INTEGER,
allowNull: false,
validate:{
trim: true
/* must convert roleId from string to integer
toInt(value){
return parseInt(value);
}*/
}
},
userId: {
type: Sequelize.INTEGER,
allowNull: false
}
},
{
timestamps: true,
versionKey: false,
hooks:{
beforeSave: async function(user,options){
if (user.isModified('password')) {
user.password = await bcrypt.hash(user.password, 8)
}
}
}
}
);
User.prototype.toJSON = function () {
const user = this;
const userObject = user.toObject();
delete userObject.password;
delete userObject.tokens;
return userObject;
};
User.prototype.generateAuthToken = async function () {
const user = this;
const payload = { user };
const token = jwt.sign(payload, process.env.ACCESS_TOKEN_SECRET, {
expiresIn: 360000
});
return token;
};
const findByCredentials = async (email, password) => {
const users= await User.findAll();
const user = await User.findOne({where: {email}});
if (!user) {
throw Error('invalid email');
}
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
throw Error('invalid password');
}
return user;
}
User.sync();
module.exports = {
User,
findByCredentials
}
I was trying to create HRM project using Node and Mongodb (Mongoose) with leave management so for the leave I have created two documents 1. for leavetypes i.e anualLeave, maternityLeave and so on and the other one of taking care of the leave requests taken by the employees.
So here is my schemas and api requests.
// leave schema embedded in leaveTypeSchema
const mongoose = require("mongoose");
const Joi = require("joi-browser");
Joi.objectId = require("joi-objectid")(Joi);
const { branchSchema } = require("./branch");
const { employeeSchema } = require("./employee");
const { leaveTypesSchema } = require("./leaveType");
const leaveSchema = mongoose.Schema({
branch: {
type: branchSchema,
required: true,
},
employee: {
type: employeeSchema,
required: true,
},
leaveType: {
type: [leaveTypesSchema],
required: true,
},
daysRequested: {
type: Number,
required: true,
},
fromDate: {
type: Date,
required: true,
},
endDate: {
type: Date,
required: true,
},
availableDays: {
type: Number,
},
});
const Leave = mongoose.model("leave", leaveSchema);
//validation
function validateLeave(leave) {
const schema = {
branchId: Joi.objectId().required(),
employeeId: Joi.objectId().required(),
leaveType: Joi.object()
.keys({
anualLeave: Joi.object()
.keys({
id: Joi.objectId().required(),
})
.required(),
})
.required(),
daysRequested: Joi.number().required(),
fromDate: Joi.date().required(),
endDate: Joi.date().required(),
};
return Joi.validate(leave, schema);
}
module.exports.Leave = Leave;
module.exports.Validate = validateLeave;
//route to post leave requests from employees
router.post("/", async (req, res) => {
// validate
const { error } = Validate(req.body);
if (error) return res.status(400).send(error.details[0].message);
// check if branch is valid
let branch = await Branch.findById(req.body.branchId);
if (!branch) return res.status(400).send("Invalid Branch");
// check if employee is valid
let employee = await Employee.findById(req.body.employeeId);
if (!employee) return res.status(400).send("Invalid employee");
// check if leaveType is valid
let leaveType = await LeaveType.findById({
id: ObjectID(req.body.leaveType.anualLeave.id),
});
if (!leaveType) return res.status(400).send("invalid leave Type");
// post the leave request
const leave = new Leave({
branch: {
_id: branch._id,
name: branch.name,
},
employee: {
_id: employee._id,
fullName: employee.fullName,
phoneNumber: employee.phoneNumber,
branch: {
_id: branch._id,
name: branch.name,
},
jobTitle: employee.jobTitle,
salary: employee.salary,
},
leaveType: [
{
anualLeave: {
id: leaveType.anualLeave.id,
},
},
],
daysRequested: req.body.daysRequested,
fromDate: req.body.fromDate,
endDate: req.body.endDate,
});
await leave.save();
res.send(leave);
Your document doesn't abide by the way you have created your schema.
When you are passing data to model, you have made leavetype nested inside employee
const leave = new Leave({
/**/
employee: {
_id: employee._id,
fullName: employee.fullName,
phoneNumber: employee.phoneNumber,
branch: {
_id: branch._id,
name: branch.name,
}, <- here
leaveType: [
{
anualLeave: {
id: leaveType.anualLeave.id,
},
},
],
});
whereas in the schema your leaveType is a diff. object property.
employee: {
type: employeeSchema,
required: true,
},
leaveType: {
type: [leaveTypesSchema],
required: true,
},
I'm using Sequelize for my Postgres database. I have a Messages and a Users table; a user has many messages and a message belongs to a user. I've defined my models as follows:
User
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
firstName: {
type: DataTypes.STRING,
allowNull: false
},
lastName: {
type: DataTypes.STRING,
allowNull: false
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true,
}
},
password: {
type: DataTypes.STRING,
allowNull: false,
validate: {
len: [8, 50],
}
},
}, {
modelName: 'User',
});
User.associate = (models) => {
User.hasMany(models.Message, { foreignKey: 'userId', as: 'Messages' })
}
return User;
};
Message
module.exports = (sequelize, DataTypes) => {
const Message = sequelize.define('Message', {
content: {
allowNull: false,
type: DataTypes.STRING,
validate: {
len: [1, 248],
}
},
userId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'Users',
key: 'id'
}
},
likes: {
defaultValue: 0,
type: DataTypes.INTEGER
},
}, {
modelName: 'Message',
});
Message.associate = (models) => {
Message.belongsTo(models.User, { foreignKey: 'userId', as: 'User', onDelete: 'CASCADE' })
}
return Message;
};
And here's how I'm testing them:
User.create({
firstName: 'Test', lastName: 'Test', email: 'test#test.com', password: '87654321'
}).then((newUser) => {
console.log(newUser.get())
})
Message.bulkCreate([
{ content: "Hello", likes: 0, userId: 1 },
{ content: "Hello World", likes: 0, userId: 1 },
{ content: "Hello World 2", likes: 123, userId: 1 }
])
.then((newMessages) => {
console.log(newMessages)
})
const findAllWithMessages = async () => {
const users = await User.findAll({
include: [{
model: Message
}]
});
console.log(JSON.stringify(users, null));
}
Here's my Migration file to create the users table:
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
firstName: {
type: Sequelize.STRING,
allowNull: false
},
lastName: {
type: Sequelize.STRING,
allowNull: false
},
email: {
type: Sequelize.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true,
}
},
password: {
type: Sequelize.STRING,
allowNull: false,
validate: {
len: [8, 50],
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Users');
}
};
And the messages table:
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('Messages', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
userId: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'Users',
key: 'id',
as: 'userId',
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
},
content: {
allowNull: false,
type: Sequelize.STRING,
validate: {
len: [1, 248],
}
},
likes: {
defaultValue: 0,
type: Sequelize.INTEGER
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Messages');
}
};
I'm registering my models and associations using Sequelize CLI out of the box code when you run sequelize-cli init:
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);
}
});
const User = require('./user')(sequelize, Sequelize.DataTypes)
const Message = require('./message')(sequelize, Sequelize.DataTypes)
db.models = { User, Message }
db.sequelize = sequelize;
db.Sequelize = Sequelize;
db.DataTypes = Sequelize.DataTypes
module.exports = db;
Finally, when I run findAllWithMessages(), I'm getting this error UnhandledPromiseRejectionWarning: SequelizeEagerLoadingError: Message is not associated to User!
I can confirm that the models are being created and that the association between the models work because when I run a raw SQL query select * from "Messages" as a inner join "Users" as b on b.id = a."userId" where a."userId"=1; I get the correct results. So I'm assuming its a Sequelize thing.
Any help is appreciated!
I've found my issue. In the code I was importing from db.models = { User, Message } so this block Object.keys(db).forEach(modelName)... wasn't associating the models I was using. Essentially, I was calling the .associate function on instances of the models that were different than the instances I was using.
When I am deleting a user, I also want to delete all the associated blog posts with that user. I have used MongoDB's pre() middleware. when it is fired it only sets the postedBy property to null in the post and then MongoDB compass the postedBy is still there along with userId
here is User schema.
const crypto = require("crypto");
const mongoose = require("mongoose");
const Post = require("./post");
const userSchema = mongoose.Schema(
{
username: {
type: String,
trim: true,
required: true,
unique: true,
index: true,
lowercase: true,
},
name: {
type: String,
index: true,
required: true,
max: 32,
},
email: {
type: String,
index: true,
required: true,
trim: true,
unique: true,
},
hashed_password: {
type: String,
required: true,
},
role: {
type: Number,
default: 0,
},
profile: {
type: String,
required: true,
},
photo: {
data: Buffer,
contentType: String,
},
salt: String,
resetPassword: {
data: String,
default: "",
},
},
{ timestamp: true }
);
userSchema
.virtual("password")
.set(function (password) {
this._password = password;
this.salt = this.makeSalt();
this.hashed_password = this.encryptPassword(password);
})
.get(function () {
return this._password;
});
userSchema.methods = {
authenticate: function (plainText) {
return this.encryptPassword(plainText) === this.hashed_password;
},
encryptPassword: function (password) {
if (!password) return "";
try {
return crypto
.createHmac("sha1", this.salt)
.update(password)
.digest("hex");
} catch (err) {
return "";
}
},
makeSalt: function () {
return Math.round(new Date().valueOf() * Math.random()) + "";
},
};
userSchema.pre("findByIdAndRemove", function (next) {
Post.deleteMany({ postedBy: this._id }, function (err, result) {
if (err) {
console.log("error");
} else {
console.log(result);
}
});
next();
});
module.exports = mongoose.model("User", userSchema);
here is Post schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const postSchema = new Schema({
postedBy: {
type: Schema.Types.ObjectId,
ref: "User",
},
title: {
type: String,
},
body: {
type: String,
},
name: {
type: String,
},
photo: {
data: Buffer,
contentType: String,
},
updated: Date,
avatar: {
type: String,
},
created: {
type: Date,
default: Date.now,
},
comments: [
{
postedBy: {
type: Schema.Types.ObjectId,
refPath: "onModel",
},
text: String,
created: {
type: Date,
default: Date.now,
},
},
],
likes: [
{
type: Schema.Types.ObjectId,
refPath: "onModel",
},
],
onModel: {
type: String,
enum: ["User", "Imam"],
},
});
module.exports = mongoose.model("Post", postSchema);
this is delete route function
exports.userdelete = (req, res) => {
User.findByIdAndRemove(req.params.id).exec((err, doc) => {
if (err) {
return res.status(400).json({
error: "Something went wrong",
});
}
return res.json({
message: "User deleted",
});
});
};
You Can follow this code
You can use cascade delete in mongoose
User.findOne({...}, function(err, customer) {
Post.remove();
});
Here is my code:
models/index.js:
var Sequelize = require('sequelize');
// initialize database connection
var sequelize = new Sequelize('somedb', '', '', {
host: 'localhost',
dialect: 'sqlite',
pool:{
max: 5,
min: 0,
idle: 10000
},
storage: "db/main.db"
});
// load models
var models = [
'Users',"Clients"
];
models.forEach(function(model) {
module.exports[model] = sequelize.import(__dirname + '/' + model);
module.exports[model].sync();
});
// export connection
module.exports.sequelize = sequelize;
app.js:
SQLized.sync().then(function(err){
//Settings Environmental Variables
var env = process.env.STAGE;
var config = require("./config.js")[env];
...
});
models/Users.js:
var Sequelize = require("sequelize");
var hash = require("../modules/util/hash");
module.exports=function(sequelize, DataTypes){
return Users = sequelize.define("Users", {
id: {
type: DataTypes.INTEGER,
field: "id",
autoIncrement: !0,
primaryKey: !0
},
firstName: {
type: DataTypes.STRING,
field: "first_name"
},
lastName: {
type: DataTypes.STRING,
field: "last_name"
},
username: {
type: DataTypes.STRING,
field: "username"
},
email: {
type: DataTypes.STRING,
field: "email"
},
password: {
type: DataTypes.STRING,
field: "password"
},
token: {
type: DataTypes.STRING,
field: "access_token"
},
isAdmin: {
type: DataTypes.BOOLEAN,
field: "isAdmin"
}
}, {
freezeTableName: true, // Model tableName will be the same as the model name
classMethods:{
usernameExists: function(username, _clb){
},
userExists: function(username, _clb){
},
signup: function(params, _clb){
var fullnm_splt = params.fullname.split(' ');
params.firstName = (fullnm_splt.length >2) ? fullnm_splt.slice(0, -1).join(" ") : fullnm_splt[0];
params.lastName = (fullnm_splt.length >2) ? fullnm_splt.slice(-1).join(" ") : fullnm_splt[1];
res.redirect("/users/" + params.username);
}
},
instanceMethods:{
isValidPassword: function(password, _clb){
var _self = this;
hash(password, function(err, password_encr){
if(err){
return _clb(err);
}
if(password_encr == _self.password){
return _clb(null, true);
}
return _clb(null, false);
});
}
}
});
};
models/Clients.js
var Sequelize = require("sequelize");
module.exports=function(sequelize, DataTypes){
return Clients = sequelize.define("Clients", {
id:{
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
},
client_name: {
type: DataTypes.STRING,
field: "client_name"
},
client_host_name: {
type: DataTypes.STRING,
field: "client_host_name"
},
ip: {
type: DataTypes.STRING,
field: "ip"
},
mac: {
type: DataTypes.STRING,
field: "mac"
},
previous_ip: {
type: DataTypes.STRING,
field: "previous_ip"
}
}, {
freezeTableName: true, // Model tableName will be the same as the model name
classMethods:{
},
instanceMethods:{
}
});
};
When I try to save something to DB, with the function below, it states the INSERT INTO query in the logs but nothing changes in the db file.
create_client:
Clients
.create({client_host_name: params.client_host_name, ip: params.ip, mac: params.mac})
.then(function(err){
res.send("OK");
});
How can I solve this?
I've tested your app, everything inserts okay check out https://github.com/finfort/SequelizeTestRepo