How to use instance methods on sequelize - javascript

Can someone help me figure out how to use my sequelize instance methods on my controller?
I wrote my model like that:
const bcrypt = require('bcryptjs');
module.exports = (sequelize, Sequelize) => {
const Patient = sequelize.define('Patient', {
email: {
type: Sequelize.STRING,
allowNull: false,
},
password : {
type: Sequelize.STRING,
allowNull: false,
},
}, {
classMethods: {
associate: (models) => {
// associations can be defined here
}
},
instanceMethods: {
generateHash: function (password) {
return bcrypt.hash(password, 8, function(err, hash){
if(err){
console.log('error'+err)
}else{
return hash;
}
});
},
validPassword: function(password) {
return bcrypt.compareSync(password, this.password);
}
}
});
return Patient;
};
but when I launch it on my controller which I made like that
const jwt = require('jsonwebtoken');
const passport = require('passport');
const Patient = require('../models').Patient;
module.exports = {
///
create(req, res) {
return Patient
.create({
email: req.body.email,
password: Patient.prototype.generateHash(req.body.password)
})
.then(patient => res.status(201).send(patient))
.catch(error => res.status(400).send(error));
},
};
I get this error for the request:
TypeError: Cannot read property 'generateHash' of undefined

First of all you should use bcrypt.hashSync() because you want to assign asynchronous function call to the password - it won't work.
generateHash: function(password){
try {
return bcrypt.hashSync(password, 8);
} catch (e) {
console.log('error: ' + e);
}
}
In order to use instance method you should do
Patient.build().generateHash(req.body.password);
build() creates new instance of model so then you can run the instance method. Or you can declare the generateHash as a class method so you could run it like that
Patient.generateHash(req.body.password);

Related

Instance methods don't work on Sequelize 4

There is model code:
'use strict';
const bcrypt = require('bcrypt');
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
}, {
hooks: {
beforeCreate: user => {
const salt = bcrypt.genSaltSync();
user.password = bcrypt.hashSync(user.password, salt);
}
},
});
User.prototype.isPasswordValid = password => {
console.log('current_email');
console.log(this.email);
//return bcrypt.compareSync(password, this.password);
};
User.associate = models => {
// associations can be defined here
};
return User;
};
When I execute this code:
const user = await User.findOne({ where: { email } });
if (!user || !user.isPasswordValid(password)) {
ctx.body = {
result: RESULT_CODE.ERROR,
error: ERROR_CODE.UNAUTHORIZED,
};
return;
}
I see the following output:
current_email
undefined
I don't understand why I can't get access to fields of user.
Versions:
"sequelize": "4.3.1",
"sequelize-cli": "4.0.0"
Try using an usual function and not an arrow function
User.prototype.isPasswordValid = function(password) {
console.log('current_email');
console.log(this.email);
//return bcrypt.compareSync(password, this.password);
};

I am trying to create a doc to model with mongoose but model.create() does not return any promise

it seems that the create method does not return any promise that then can handle
I tried different things but nothing worked
this is my routes file
const express = require("express")
const router = express.Router();
const controller = require("./controller")
router.post("/signup", controller.create);
module.exports = router;
and this is my model file
const mongoose = require('mongoose');
const User = new mongoose.Schema(
{
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
picture: {
type: String
},
password: {
type: String,
select: false
},
email: {
required: true,
type: String,
unique: true
}
},
{
timestamps: true
}
);
User.index({
firstName: 'text',
lastName: 'text',
});
module.exports = mongoose.model('User', User);
and this is the controller file
const User = require('./model');
const { hash, compareHash } = require('../lib/util');
const { createToken, findUserByToken } = require('../lib/auth');
const cookieIsSecure = process.env.ENVIRONMENT === 'production';
exports.create = async (req, res) => {
const password = await hash(req.body.password);
const rawUser = {
...req.body,
password,
};
User.create(rawUser)
.then(async user => {
return user.save();
})
.then(async user => {
const newUser = user.toObject();
res.send(newUser);
})
.catch(err => {
if (err.code === 11000) {
res.status(400).send({ message: 'A user with this email address has already registered.' });
return;
}
res.status(500).send({ message: 'An unexpected error occurred' });
});
};
it always return the 500 error "an unexpected error occurred"
which is not really specific. and i do not know what is the problem exactly. but I am sure it has something to do with the model.create() it does not return any promise.
Here you are mixing methods. create doesn't want save in it as it's implicit:
https://mongoosejs.com/docs/api.html#model_Model.create
Please try this, I've refactored your code a bit and added much easier to read and use try/catch:
const rawUser = new User({ ...req.body, password});
try {
await rawUser.save();
res.status(201).send(newUser);
} catch(err) {
if (err.code === 11000) return res.status(400).send({ message: 'A user with this email address has already registered.' });
res.status(500).send({ message: 'An unexpected error occurred' });
}
You need to use async/await like this:
exports.create = async (req, res) => {
try {
const password = await hash(req.body.password);
const rawUser = {
...req.body,
password
};
const user = await User.create(rawUser);
const newUser = user.toObject();
res.send(newUser);
} catch (err) {
console.log("ERROR: ", err);
if (err.code === 11000) {
return res.status(400).send({
message: "A user with this email address has already registered."
});
}
res.status(500).send({ message: "An unexpected error occurred" });
}
};

Soft delete using nodejs + mongodb

How do i perform a soft delete using nodejs on mongodb
for example using this code, can it be modified to do a soft delete instead or is there another way?
Controllers/ category.js
exports.remove = (req, res) => {
const category = req.category;
category.remove((error, data) => {
if (error) {
return res.status(400).json({
error: errorHandler(error)
});
}
res.json({
message: "Category deleted"
});
});
};
routes/category.js
const express = require("express");
const router = express.Router();
const { create, categoryById, read, update, remove, list } = require("../controllers/category");
const { requireSignin, isAuth, isAdmin } = require("../controllers/auth");
const { userById } = require("../controllers/user");
router.get("/category/:categoryId", read);
router.post("/category/create/:userId", requireSignin, isAuth, isAdmin, create);
router.put("/category/:categoryId/:userId", requireSignin, isAuth, isAdmin, update);
router.delete("/category/:categoryId/:userId", requireSignin, isAuth, isAdmin, remove);
router.post("/categories", list);
router.param("categoryId", categoryById);
router.param("userId", userById);
module.exports = router;
models/category.js
const mongoose = require("mongoose");
const categorySchema = new mongoose.Schema(
{
name: {
type: String,
trim: true,
required: true,
maxlength: 32
}
},
{ timestamps: true }
);
module.exports = mongoose.model("Category", categorySchema);
I'm not sure req.category is instance of model or model itself.
So in my answer below I assume that somehow You've got instance of model and injected it as req.category
1) Add deleted field to schemas where You want to have soft delete:
const mongoose = require("mongoose");
const {Schema} = mongoose;
const categorySchema = new Schema(
{
name: {
type: Schema.Types.String,
trim: true,
required: true,
maxlength: 32
},
// deleted flag for soft delete feature
deleted: {
type: Schema.Types.Boolean,
index: true,
default: false
}
},
{ timestamps: true }
);
module.exports = mongoose.model("Category", categorySchema);
2) Change delete procedure:
module.exports.remove = async (req, res) => {
try {
const category = req.category; // if it's an instance of model
// or if it's mongoose model comment line above
// const category = await req.category.findOne({
// _id: req.params.categoryId,
// deleted: false
// });
if (!category || category.deleted === true) {
return res.status(404).json({
error: 'Requested category does not exist'
});
}
category.deleted = true;
await category.save();
res.status(200).json({
message: "Category deleted"
});
}
catch (error) {
res.status(400).json({
error: errorHandler(error)
});
}
};
3) Change category read route: /category/:categoryId handler:
module.exports.read = async (req, res) => {
try {
const category = req.category; // if it's an instance of model
// or if it's mongoose model comment line above
// const category = await req.category.findOne({
// _id: req.params.categoryId,
// deleted: false
// });
if (!category || category.deleted === true) {
return res.status(404).json({
error: 'Requested category does not exist'
});
}
res.status(200).json(category);
}
catch (error) {
res.status(400).json({
error: errorHandler(error)
});
}
};
4) Change listing procedure:
module.exports.list = async (req, res) => {
try {
const categories = await req.category.find({deleted: false});
res.status(200).json({categories});
}
catch (error) {
res.status(400).json({
error: errorHandler(error)
});
}
};
Step 1:
Add a new field in your Schema "Deleted" with type Boolean and Default value 'false'
deleted: { type: Boolean, default: false }
Step 2
Change Delete Process To:
router.delete('/:id', verifyTokenAndAuthorization, async (req, res) => {
try {
await User.findByIdAndUpdate(req.params.id, { deleted: true }); <= change delete status to 'true'
res.status(200).json('user Deleted');
} catch (error) {
res.status(500).json(error)
}
})

TypeError: User.findByIdAndUpdate is not a function

Answer
this fixed: mongoose.model('User').findByIdAndUpdate(... although it doesn't seem right. Should be another way to require this. Will figure out.
I have a signup form, where user can check a role to either create a candidate or an employer account. Let's say, user picked employer, then mongoose post hook will be triggered to create an employer inside employer collection and return employer._id so that I can save it to user collection as reference. In the last code snippet, findByIdAndUpdate is not triggered and failed silently. Log, outputs only a.
user.js (user model)
const mongoose = require('mongoose');
const { Schema } = mongoose;
const bcrypt = require('bcrypt-nodejs');
const {
updateUserWithEmployerId,
updateUserWithCandidateId
} = require('../api/user');
const { createCandidate } = require('../api/candidate');
const { createEmployer } = require('../api/employer');
const userSchema = new Schema({
firstName: String,
lastName: String,
email: {
type: String,
unique: true,
lowercase: true
},
password: String,
role: {
type: String,
enum: ['candidate', 'employer']
},
_employerId: {
type: Schema.Types.ObjectId,
ref: 'Employer'
},
_candidateId: {
type: Schema.Types.ObjectId,
ref: 'Candidate'
}
}, {
timestamps: true
});
userSchema.post('save', function(doc, next){
if(doc.role === 'employer'){
return createEmployer(doc._id)
.then(response => updateUserWithEmployerId(doc._id, response.employer._id))
// .catch(error => next(error))
// .catch(error => response.status(500).send(error))
}else if(doc.role === 'candidate'){
return createCandidate(doc._id)
.then(response => updateUserWithCandidateId(doc._id, response.candidate._id))
.catch(error => response.status(500).send(error))
}else{
throw new Error('User role not found!');
}
next();
});
const ModelClass = mongoose.model('User', userSchema);
module.exports = ModelClass;
employer.js(this is not model)
const Employer = require('../models/employer');
exports.createEmployer = (userId) => {
return new Promise(function(resolve, reject){
const employerInstance = new Employer({ _userId: userId });
if(employerInstance){
employerInstance.save((err, employer) => {
if(err){
reject({ message: "Error occured while creating employer!" });
}else{
resolve({ employer: employer });
}
});
}else{
reject({ message: "Error occured while creating employer!" });
}
});
};
user.js (not a model)
const User = require('../models/user');
exports.updateUserWithEmployerId = (userId, employerId) => {
return new Promise(function(resolve, reject){
console.log("a");
User.findByIdAndUpdate(userId, { _employerId: employerId }, { new: true },
(err, user) => {
console.log("user:", user);
if(err){
console.log("b");
reject({ message: "Error occured while updating user with employer id!" });
}else{
console.log("c");
resolve({ user: user });
}
});
console.log("d");
});
};
Server is working on http:4000
Mongodb connected with server: localhost
C:\Ajay Vemra\Mern Stack E-Commerce Website\backend\controllers\productController.js:41
product = await product.findByIdAndUpdate(req.params.id,req.body,{
^
TypeError: product.findByIdAndUpdate is not a function
at exports.updateProduct (C:\Ajay Vemra\Mern Stack E-Commerce Website\backend\controllers\productController.js:41:29)

Error with Sequelize v4

I have just coding the todolist api project on NodeJS & Express. I follow some instruction using Sequelize to interact with DB: SQLite. But I encounter with Sequelize to create class method as below:
user.js
var bcrypt = require('bcrypt');
var _ = require('underscore');
module.exports = (sequelize, DataTypes) => {
var User = sequelize.define('user', {
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true
}
},
salt: {
type: DataTypes.STRING
},
password_hash: {
type: DataTypes.STRING
},
password: {
type: DataTypes.VIRTUAL,
allowNull: false,
validate: {
len: [6, 100]
},
set: function (value) {
var salt = bcrypt.genSaltSync(10);
var hashedPassword = bcrypt.hashSync(value, salt);
this.setDataValue('password', value);
this.setDataValue('salt', salt);
this.setDataValue('password_hash', hashedPassword);
}
}
}, {
hooks: {
beforeValidate: (user, options) => {
if (typeof user.email === 'string') {
user.email = user.email.toLowerCase();
}
}
}
});
return User;
// Class methods
User.prototype.toPublicJSON = function() {
var json = this.toJSON();
return _.pick(json, 'id', 'email', 'createdAt', 'updatedAt');
};
User.authenticate = (body) => {
return new Promise ((resolve, reject) => {
if (typeof body.email !== 'string' || typeof body.password !== 'string') {
return reject();
}
user.findOne({
where: {
email: body.email
}
}).then((user) => {
if (!user || !bcrypt.compareSync(body.password, user.get('password_hash'))) {
return reject();
}
resolve(user);
}, (e) => {
reject();
})
});
};
}
db.js
var Sequelize = require('sequelize');
var sequelize = new Sequelize(undefined, undefined, undefined, {
'dialect': 'sqlite',
'storage': __dirname + '/data/dev-todo-api.sqlite'
});
db = {};
db.todo = sequelize.import(__dirname + '/models/todo.js');
db.user = sequelize.import(__dirname + '/models/user.js');
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
user.js
app.post('/users/login', (req, res) => {
var body = _.pick(req.body, 'email', 'password');
db.user.authenticate(body).then((user) => {
res.json(user.toPublicJSON());
}, () => {
res.status(401).send();
});
})
Error: db.user.authenticate is not function.
I think I can use function authenticate after user.js return variable User. Please advise me how to resolve this problem. Thanks all.
The problem is not with sequelize. Rather you are defining the methods after return, so the code that is responsible for creating the methods is never reached.
return User;
// Class methods
User.prototype.toPublicJSON = function() {
var json = this.toJSON();
return _.pick(json, 'id', 'email', 'createdAt', 'updatedAt');
};
User.authenticate = (body) => {
You should move the return User statement at the end of your arrow function and your code should work.

Categories