I am a nodeJs developer, I failed to get the list of users, when I tested my code on postman it gives me the error below:
User.find is not a function
The following is my user.js file
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class User extends Model {
static associate(models) {
}
}
User.init({
username: {
type:DataTypes.STRING,
allowNull: false,
},
email: {
type:DataTypes.STRING,
allowNull: false,
},
password: {
type:DataTypes.STRING,
allowNull: false,
},
role: {
type:DataTypes.STRING,
allowNull: false,
},
}, {
sequelize,
tableName:'users',
modelName: 'User',
});
return User;
};
and this is where it's imported/required
const router = require('express').Router();
let User = require('../models/user');
router.route('/getUser').post((req, res) => {
User.find({})
.then(users => res.json(users))
.catch(err => res.status(400).json('Error: ', err));
});
The user.js file that you provided is exporting a function that create the User class (and not actually the User model class)
Did you already init this class elsewhere and you are importing the wrong file ?
It didn't work, I did as you told me but it gives me back:
sequelize id not defined
Also I don't think that the problem is in the model because I have used it before and in the login I used .find and it worked without any problem!
this login code :
router.post('/login', async (req,res,next) => {
const user = await User.findOne({ where: { email: req.body.email }});
if (user) {
const password_valid = await bcrypt.compare(req.body.password,user.password);
if (password_valid) {
token = JWT.sign({
isLogin: true,
"id": user.id,
"email": user.email,
"username": user.username
},"12345678");
res.status(200).json({token :token});
} else {
res.status(400).json({isLogin : false, error: "password incorrect" });
}
} else {
res.status(404).json({isLogin : false, error :"User does not exist"});
}
});
Related
I've defined a user model in Sequelize, and also defined a custom class method and an instance method for it. I'm calling these two methods in my login api (which works fine). The problem is that the class method works perfectly, but the instance method results an error, and I cannot recognize what is wrong with my code. PLEASE HELP.
This is my user model and its methods:
const Sequelize = require("sequelize");
const sequelize = require("../db/db.config");
const bcrypt = require("bcryptjs");
const _ = require("lodash");
const jwt = require("jsonwebtoken");
const User = sequelize.define("user", {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true,
},
first_name: {
type: Sequelize.STRING(50),
allowNull: false,
},
last_name: {
type: Sequelize.STRING(50),
allowNull: false,
},
email: {
type: Sequelize.STRING(50),
allowNull: false,
validate: {
isEmail: true,
},
},
password: {
type: Sequelize.STRING(100),
allowNull: false,
},
});
User.prototype.testMethod = function () {
console.log("THIS IS A TEST");
};
User.beforeCreate(async (user, options) => {
const hashedPassword = await bcrypt.hash(user.password, 10);
user.password = hashedPassword;
});
User.findByEmailAndPassword = async function (inputEmail, inputPassword) {
try {
const user = await User.findOne({ where: { email: inputEmail } });
if (user === null) {
return null;
}
const passwordMatch = await bcrypt.compare(inputPassword, user.password);
if (!passwordMatch) {
return null;
}
return _.pick(user, "id", "first_name", "last_name", "email");
} catch (error) {
console.log("FIND BY EMAIL AND PASSWORD ERROR: ", error);
}
};
module.exports = User;
And this is my login router:
const express = require("express");
const router = express.Router();
const User = require("../models/user.model");
router.post("/api/login", async (req, res) => {
try {
const user = await User.findByEmailAndPassword(
req.body.email,
req.body.password
);
console.log("USER: ", user);
await user.testMethod();
if (!user) {
return res.status(400).send({
errorMessage: "Username and password combination is not correct!",
});
}
return res.status(200).send(user);
} catch (error) {
res.status(400).send({ errorMessage: error });
}
});
module.exports = router;
Thanks.
First, the reason that you get the issue is that findByEmailAndPassword is returning the regular object from _.pick and you are defining the instance method for Sequelize instance. This instance method can be callable on Sequelize instance and not on regular object.
However, your goal is
What I'm trying to do here is to avoid sending user password in my response body.
defaultScope is great for this use case. It allows you to define some repetitive options on a model.
You can define your User model as
const User = sequelize.define("user", {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true,
},
...
}, {
defaultScope: {
attributes: {
exclude: ['password']
}
}
});
Defining the defaultScope on the model, this will be applied to many Sequelize functions by default.
Scopes apply to .find, .findAll, .count, .update, .increment and .destroy.
I also tested that it also applied to .findByPk, .findOne.
So, how to use...
Call regular Sequelize findOne function.
const user = User.findOne({
where: {
email: req.body.email,
password: req.body.password
}
});
By default, since defaultScope is applied, this won't return password in response.
In some scenarios where you need to return the password, use unscoped to disable the defaultScope.
// This will return `password` in response.
User.unscoped().fineOne(...)
For reference: https://sequelize.org/master/manual/scopes.html
So i was implementing a bit of change in my server application - switching databases from MongoDb to PostgreSQL (with Sequelize 6) and i had to change the controller functions i had created for mongoose to suit the current database but there was a problem implementing the instance methods, but as usual there was little to no helpful solutions online for this with Sequelize 6. But now there is. Below are the code samples for some of the problems and error messages you may be facing if you come across this post.
The function which calls the instance method:
userController.js (login function)
User.findByPk(req.body.id)
.then(user => {
if (!user) {
return res.status(401).send('Sorry!! You do not have an account with us.')
}
if (!user.validPassword(req.body.password)) {
return res.status(401).send('Invalid Password')
} else {
res.status(200).json({ user })
}
})
.catch(error => {
console.log(error)
res.status(500).send({
message: 'Some error occurred while logging in this User',
error: error.message
});
});
EXAMPLE CODE 1
user.js
'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class User extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
};
User.init({
username: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password_confirmation: {
type: DataTypes.STRING,
allowNull: false,
unique: true
}
}, {
hooks: {
beforeCreate: (User) => {
const salt = bcrypt.genSaltSync();
User.password = bcrypt.hashSync(User.password, salt);
User.password_confirmation = User.password;
}
},
instanceMethods: {
validatePassword: (password) => {
return bcrypt.compareSync(password, this.password);
}
}
sequelize,
modelName: 'User',
});
return User;
};
response (Server 500 error message)
{
"message": "Some error occurred while logging in this User",
"error": "user.validPassword is not a function"
}
The instance method in this case is not recognized and the function validPassword() is not run thus a 500 Server error. Let's move to example 2.
EXAMPLE CODE 2
user.js
'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class User extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
};
User.init({
username: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password_confirmation: {
type: DataTypes.STRING,
allowNull: false,
unique: true
}
}, {
hooks: {
beforeCreate: (User) => {
const salt = bcrypt.genSaltSync();
User.password = bcrypt.hashSync(User.password, salt);
User.password_confirmation = User.password;
}
},
instanceMethods: {
validatePassword: (password) => {
return bcrypt.compareSync(password, this.password);
}
}
sequelize,
modelName: 'User',
});
User.prototype.validPassword = (password) => {
return bcrypt.compareSync(password, this.password);
};
return User;
};
response (Server 500 error message)
{
"message": "Some error occurred while logging in this User",
"error": "Illegal arguments: string, undefined"
}
The instance method in this case is still not recognized and the function validPassword() is thus not run because over here the parameter this.password for the bcrypt.compareSync() function is not defined (or has been used outside the Model extension) thus a 500 Server error.
And now for the solution.
After around half a day of searching, I found out that for some reason the instanceMethods functionality has been removed in Sequelize v4. As a result, the only way to obtain this functionality is by one of the following:
declaring the function on the model class as Jeffrey Dabo suggested
adding the function on the prototype of the Sequelize model
Very important: If you go with the prototype approach, in order to have access to the this object, you need to declare the function using the function syntax and not as an arrow function, or else it will not work.
Example:
const User = sequelize.define('User', {...});
User.prototype.validatePassword = function (password) {
return bcrypt.compareSync(password, this.password);
}
Placing the instance method function right under the class method function (associations on models) would eventually allow your function validPassword() to be recognized, run and produce the desired response.
user.js
'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class User extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
validPassword(password) => {
return bcrypt.compareSync(password, this.password);
};
};
User.init({
username: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password_confirmation: {
type: DataTypes.STRING,
allowNull: false,
unique: true
}
}, {
hooks: {
beforeCreate: (User) => {
const salt = bcrypt.genSaltSync();
User.password = bcrypt.hashSync(User.password, salt);
User.password_confirmation = User.password;
}
},
instanceMethods: {
validatePassword: (password) => {
return bcrypt.compareSync(password, this.password);
}
}
sequelize,
modelName: 'User',
});
return User;
};
I don't think this will help you but I can see that you have a syntax error in your codes, try adding , before
sequelize, modelName: 'User'
{
hooks: {
beforeCreate: (User) => {
const salt = bcrypt.genSaltSync();
User.password = bcrypt.hashSync(User.password, salt);
User.password_confirmation = User.password;
}
},
instanceMethods: {
validatePassword: (password) => {
return bcrypt.compareSync(password, this.password);
}
},
sequelize,
modelName: 'User',
}
I've been trying to delete a user in a MySql database using Sequelize ORM but it's not deleting.
without paranoid, here's the query generate:
UPDATE `Users` SET `active`=?,`updatedAt`=? WHERE `id` = ?
with paranoid set to true, here's the query generate:
UPDATE `Users` SET `active`=?,`updatedAt`=? WHERE (`deletedAt` IS NULL AND `id` = ?)
digging around the internet i found out that the the query is supposed to take this form UPDATE "posts" SET "deletedAt"=[timestamp] WHERE "deletedAt" IS NULL AND "id" = 1 accounding to this Documentation.
Here's my model:
'use strict';
const sequelizePaginate = require('sequelize-paginate')
const bcrypt = require('bcrypt')
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
photo: DataTypes.STRING,
firstName: DataTypes.STRING,
lastName: DataTypes.STRING,
email: DataTypes.STRING,
phone: DataTypes.STRING,
password: DataTypes.STRING,
role: DataTypes.STRING,
verifyEmailAt: DataTypes.DATE,
referralCode: DataTypes.STRING,
active: DataTypes.BOOLEAN,
referrer: DataTypes.STRING,
rememberToken: DataTypes.STRING
}, { instanceMethods: {
comparePasswords: (password, prevPassword,callback) => {
bcrypt.compare(password, prevPassword, (error, isMatch) => {
if(error) {
return callback(error);
}
return callback(null, isMatch);
});
}
},
hooks: {
beforeValidate: (user) => {
if(user.changed('password')) {
return bcrypt.hash(user.password, 10).then(function(password) {
user.password = password;
});
}
}
}});
User.associate = (models) => {
// associations can be defined here
models.User.hasMany(models.Booking)
models.User.hasMany(models.DriversLocation)
models.User.hasMany(models.DriverReview)
models.User.hasMany(models.Payment)
};
sequelizePaginate.paginate(User)
return User;
};
And also my query snippet:
const { validationResult } = require('express-validator')
const models = require('../models')
deleteAdmin: (req, res) => {
const result = validationResult(req);
if (!result.isEmpty()) {
return res.status(422).json({ success: false, message: 'input validation failure',
code: 422, properties:{ userId : req.params.userId}, error: result.array() });
}
models.User.destroy({
where: {
id: req.params.userId
}
})
.then((data) => {
return res.status(200).json({ success: true, message: "success",
code: 200, properties:{params: {userId: req.params.userId }},
data: {content: data}});
}).catch((error) => {
return res.status(520).json({ success: false, message: "unknown error",
code: 520, properties:{params: {userId: req.params.userId }},
data: { error: error} });
})
}
Is there something am doing wrong here?
Thank you for your help in advance
destroy usage seems fine to me. Please verify your User model import & req params values.
Also, there is no id field defined in schema definition. How is id being saved in DB record ?
Right now I'm hashing the password on the route function and providing the hash when creating the user but I know there's a way to have this be handled through Sequelize itself. I have searched around but every answer seems to be outdated or the methods never got called. Here is my config:
server/models/User.js
module.exports = (sequelize, type) => {
const User = sequelize.define(
"User",
{
user_id: {
type: type.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: type.STRING,
allowNull: false
},
email: {
type: type.STRING,
allowNull: false
},
password: {
type: type.STRING,
allowNull: false
},
reg_date: {
type: type.DATEONLY,
allowNull: false,
defaultValue: sequelize.fn("now")
}
},
{
timestamps: false
}
);
return User;
};
server/config/sequelize.js
const Sequelize = require("sequelize");
const sequelize = new Sequelize(process.env.CLEARDB_DATABASE_URL);
sequelize
.authenticate()
.then(() => {
console.log("Connection has been established successfully.");
})
.catch(err => {
console.error("Unable to connect to the database:", err);
});
const UserModel = require("../models/User");
const User = UserModel(sequelize, Sequelize);
module.exports = User;
This is how I'm handling hashing right now:
server/routes/register.js
User.findOne({ where: { email: email } }).then(user => {
if (!user) {
bcrypt.hash(password, 10, (err, hash) => {
if (err) throw err;
User.create({
name: req.body.name,
email: email,
password: hash
})
.then(user => {
return user;
})
.catch(err => console.log(err));
});
}
});
This did the trick:
...
{
timestamps: false
}
);
User.addHook(
"beforeCreate",
user => (user.password = bcrypt.hashSync(user.password, 10))
);
return User;
};
Please don't mix your model definition with a business or a security logic (or another one). In future you might want to change an encryption library or a hashing algorithm and you will have to change your model accordingly. A security layer should be separated from your models.
I am using this scotch tutorial to create a basic node application using the sequelize js for ORM, here is my models/index.js:
and I am unable to use my user model with the user controller:
my model :
'use strict';
module.exports = (sequelize, DataTypes) => {
const user = sequelize.define('user', {
name: {
type : DataTypes.STRING,
allowNull : false
},
email: {
type: DataTypes.STRING,
allowNull: false
},
password: {
type: DataTypes.STRING,
allowNull: false
}
}, {});
user.associate = function(models) {
user.hasMany(models.wish, {
foreignKey: 'user_id',
as: 'wishes'
});
};
return user;
};
and here is my controller:
const user = require('../models').user;
module.exports = {
create(req, res){
return user.create({
name: req.body.name,
email: req.body.email,
password: req.body.password }).then(
todo => res.status(201).send(user)
).catch(
error => res.status(400).send(error)
);
}
}
but for some reason a big error comes on my screen and crashes the application, what I diagnosed from that error is that my controller is not able to find the 'user' model AND and I tried to link a bunch of things like giving the path to user directly but it did not work.
here is the error if you need (I am a newbee so could be totaly wrong about this)
I think you should change this line :
const user = require('../models').user;
to
const user = require('../models');