The classMethods findMyMobile() when called from contoller is giving error - node.js

I have created number of function in the user model which are both instance and class methods. But when i am calling class method findMyMobile() from a controller it is giving 'not a function' error. I tried display it inside the controller but it seems it is undefined there.
model/user.js
const { Sequelize, sequelize } = require('../db/sequelize');
const jwt = require('jsonwebtoken');
const Model = Sequelize.Model;
class User extends Model {}
User.init({
id:{
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: Sequelize.STRING,
allowNull: false
},
mobile_number:{
field:'mobile_number',
type: Sequelize.BIGINT(10),
unique:true,
allowNull: false,
is:/^[1-9]\d{9}$/g,
},
type:{
type: Sequelize.ENUM('0','1','2'),
allowNull: false,
defaultValue: '1',
},
otp:{
type: Sequelize.STRING,
},
createdAt: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW
},
updatedAt: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW
}
},{ sequelize,
modelName:'user',
classMethods:{
findByMobile: function(){
var User = this;
return User.findOne({'mobile_number':data['mobile_number']}).then(user => {
return new Promise((resolve,reject)=>{
if(user)
resolve(user);
else
reject(new Error('No user found'));
});
}).catch(err=>{
return Promise.reject(new Error('Database error'));
})
}
}
})
User.sync();
module.exports = {
User
}
controller/login.js
const { User } = require('../model/user');
const _ = require('lodash');
exports.login = (req, res) => {
const mobile = _.pick(req.body, ['mobile_number']);
console.log(typeof User.findByMobile);
User.findByMobile(mobile).then((user) => {
console.log(user);
}).catch(err => {
var response = {
status: 'failure',
message: err.message
}
res.send(response);
});
};
ERROR:
TypeError: User.findByMobile is not a function

Since sequelize v4 classMethods and instanceMethod are removed from the configuration options : https://sequelize.org/v4/manual/tutorial/upgrade-to-v4.html#config-options
You have two ways to define them
class User extends Model {
//Class Method
static findByMobile() {}
//Instance Method
findByMobile() {}
}
OR
class User extends Model { }
//Class Method
User.findByMobile = function() {}
//Instance Method
User.prototype.findByMobile = function() {}

I think you have the export wrong. See this example for reference.
module.exports = {
getName: () => {
return 'Jim';
},
getLocation: () => {
return 'Munich';
},
dob: '12.01.1982',
};
Then on the import file:
const { getName, dob } = require('./user');
console.log(
`${getName()} was born on ${dob}.`
);
What I do suggest is export the function itself. See link below for ref:
What is the purpose of Node.js module.exports and how do you use it?

Related

Error: Include unexpected. Element has to be either a Model, an Association or an object. in sequelize

enter image description here this is my database with three tables
1.users (user has one account).
2.Account (account has many transactions).
3.Transactions.
my Account table
module.exports = (sequelize, DataTypes) => {
const Account = sequelize.define("Account", {
UserId: {
type: DataTypes.INTEGER,
primaryKey: true,
allowNull: true,
},
UUID: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
},
amount: {
type: DataTypes.FLOAT,
defaultValue: 0,
allowNull: false,
},
});
Account.associate = function(models) {
Account.hasMany(models.Transaction, {
foreignKey: 'UserId',
targetKey: 'UUID'
});
};
Account.associate = function (models) {
Account.belongsTo(models.Users,{
foreignKey: "UserId",
})
}
return Account;
};
my transaction table
module.exports = (sequelize, DataTypes) => {
const Transaction = sequelize.define("Transaction", {
UUID: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
},
mode: {
type: DataTypes.ENUM("debit", "credit"),
defaultValue: "debit",
},
balance: {
type: DataTypes.BIGINT,
allowNull: false,
},
day: {
type: DataTypes.INTEGER,
allowNull: false,
unique: false,
},
});
// Transaction.associate = function (models) {
// Transaction.belongsTo(models.Account);
// };
Transaction.associate = function(models) {
Transaction.belongsTo(models.Account, {
foreignKey: 'UserId',
});
};
return Transaction;
};
my route to select accounts associated to transactions
const express = require("express");
const router = express.Router();
const { Account } = require("../models");
const {Transactions} = require("../models")
router.get("/", async (req, res) => {
try {
const users = await Account.findAll({
include:[
{
modal : Transactions,
}
]
});
res.json(users);
} catch (error) {
console.error(error);
res.status(500).send({ err: "Error fetching users",error: error });
}
});
i am facing this error
Error: Include unexpected. Element has to be either a Model, an Association or an object.
at Account._conformInclude (S:\test\server\node_modules\sequelize\lib\model.js:323:11)
at S:\test\server\node_modules\sequelize\lib\model.js:270:61
at Array.map (<anonymous>)
at Account._conformIncludes (S:\test\server\node_modules\sequelize\lib\model.js:270:39)
at Account._baseMerge (S:\test\server\node_modules\sequelize\lib\model.js:622:10)
at Account._defaultsOptions (S:\test\server\node_modules\sequelize\lib\model.js:656:17)
at Account._injectScope (S:\test\server\node_modules\sequelize\lib\model.js:2001:10)
at Account.findAll (S:\test\server\node_modules\sequelize\lib\model.js:1105:10)
at S:\test\server\routes\Transactions.js:8:33 at Layer.handle [as handle_request] (S:\test\server\node_modules\express\lib\router\layer.js:95:5)
i want i have tried to get users and accounts it works but account and their transactions failed please help
this is working
const { Op } = require('sequelize');
const express = require("express");
const router = express.Router();
const { Account, Users } = require("../models");
router.get("/", async (req, res) => {
try {
const accounts = await Users.findAll({
attributes: ["fullName", "status"],
include: [
{
model: Account,
where: {
UserId: {
[Op.not]: null
}
}
},
],
});
res.json(accounts);
} catch (error) {
console.log(error);
res.status(500).send({ error: error });
}
});
Thanks you so much

How to create a Many to Many association in sequelize and nodejs

I am trying to create a many to many relationship between user table and role table through userroles table.
After table creation the db looks just fine, I tried pretty much everything I found on the sequelize documentation and previous answers here, nothing seems to work.
I am getting this error: EagerLoadingError [SequelizeEagerLoadingError]: UserRoles is not associated to User!
Any idea of what am I doing wrong ? please help!
class User extends Model {
static associate(models) {
User.belongsToMany(models.Role, {
foreignKey: "user_id",
through:'UserRoles',
as:"users"
});
}
}
User.init(
{
user_id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
unique: true,
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
phone: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
sequelize,
modelName: "User",
}
);
class Role extends Model {
static associate(models) {
Role.belongsToMany(models.User, {
foreignKey: "role_id",
through:'UserRoles',
as:"roles"
});
}
}
Role.init(
{
role_id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
unique:true
},
role_name: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
role_desc: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
sequelize,
modelName: "Role",
}
);
class UserRoles extends Model {
static associate(models) {
}
}
UserRoles.init(
{
userroles_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
// user_id: {
// type: DataTypes.UUID,
// defaultValue: DataTypes.UUIDV4,
// },
// role_id: {
// type: DataTypes.UUID,
// defaultValue: DataTypes.UUIDV4,
// },
},
{
sequelize,
modelName: "UserRoles",
}
);
const signup = (req, res) => {
console.log(req.body);
console.log("signup entry");
if (
!req.body.role ||
!req.body.email ||
!req.body.password ||
!req.body.name ||
!req.body.phone
) {
res.status(400).send({
msg: "Please pass role, email, password and name.",
});
} else {
sequelize.models.User.findOne({
where: {
email: req.body.email,
},
})
.then((duplicateemailfound) => {
if (duplicateemailfound) {
console.log(duplicateemailfound);
return res.status(400).json({
success: false,
message: "Email already registered",
});
} else {
let userRole = req.body.role.toLowerCase();
console.log("userRole:", userRole);
sequelize.models.Role.findOne({
where: {
role_name: userRole,
},
})
.then((foundRole) => {
// console.log(foundRole);
if (foundRole == null) {
return res.status(400).json({
success: false,
role: "null or not found",
});
}
// console.log("foundRole", foundRole); // .role_id
let addUser = {
email: req.body.email,
password: req.body.password,
name: req.body.name,
phone: req.body.phone,
role_id: foundRole.role_id,
};
sequelize.models.User.create(addUser, {
include: [{ model: sequelize.models.UserRoles }],
})
.then((newUser) => {
console.log("new user", newUser);
return res.status(201).json({
success: true,
newuser: newUser,
});
})
.catch((error) => {
console.log(error);
res.status(400).json({
success: false,
// message: "Duplicate Email was Found",
error: error.errors[0].message,
error: error,
});
});
})
.catch((error) => {
console.log(error);
res.status(400).json({
error: error,
msg: "bbb",
});
});
}
})
.catch((err) => {
console.log(err);
});
}
};
You create some a class for each Model and extend them with Model class of sequelize, this if fine.
Now, you define a static method inside the class named associate(model) where you define the rule for that class. This is fine because you used static which is required here to since it will be a property of the class, not of an object.
Then you call the initialize method (a in-built method of class Model). In the same way you need to call your defined associate.
Here is a problem, because in the structure that you have now, you can't call that method in it's own class file, becuase you need the other Model to pass it as parameter.
So there are 2 solutions:
Import your User model inside Role model file and call the static method, like this:
const User = require('User')
class Role extends Model {
static associate(model) {
Role.belongsToMany(model, {
foreignKey: "role_id",
through:'UserRoles',
as:"roles"
});
}
}
Role.init(
{
role_id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
unique:true
},
role_name: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
role_desc: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
sequelize,
modelName: "Role",
}
);
Role.associate(User);
This will use your User model to pass it to the static method and finally to run the belongsToMany
Second solution would be to create an index file for your Models, where you import both of them and you can simply run that belongsToMany there, and then simply import that index file in the main file of your application, something like this:
User.js
const index = require('./../index.js');
const Sequelize = require('sequelize');
const Model = Sequelize.Model;
const sequelize = index.sequelize;
class User extends Model{}
User.init({
username: {
type: Sequelize.STRING,
allowNull: false,
unique: true
},
password: {
type: Sequelize.STRING,
allowNull: false
},
role: {
type: Sequelize.STRING,
allowNull: false
}
},{
sequelize,
modelName: 'user'
});
module.exports = {
User: User
}
Role.js will look the same but with your own model.
and index.js would look like this:
const Sequelize = require('sequelize');
const sequelize = new Sequelize(process.env.DB_NAME, process.env.DB_USERNAME, process.env.DB_PASSWORD, {
host: process.env.DB_HOST,
dialect: process.env.DB_DIALECT
});
exports.sequelize = sequelize;
const user = require('./models/User');
const role= require('./models/Role');
role.belongsToMany(user, {
foreignKey: "role_id",
through:'UserRoles',
as:"roles"
});
sequelize.sync(user);
sequelize.sync(role);
exports.db = {
user: user,
role: role
}

Cannot read property 'findOne' of undefined"

I am writing an endpoint that would create a record but before then, I want to be able to validate if one of those data exist before allowing the data to be saved. This is being done using the custom validator in express-validator. I am also using Sequelize as well.
My migration files looks like this:
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('merchant_temp', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
firstname: {
type: Sequelize.STRING,
allowNull: false
},
lastname: {
type: Sequelize.STRING,
allowNull: false
},
phone: {
type: Sequelize.STRING,
allowNull: false,
unique: true
},
email: {
type: Sequelize.STRING,
allowNull: false,
unique: true
},
password: {
type: Sequelize.STRING,
allowNull: false
},
ip_address: {
type: Sequelize.STRING
},
created_at: {
allowNull: false,
type: Sequelize.DATE
},
updated_at: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('merchant_temp');
}
};
Model file (merchanttemp.js)
'use strict';
const {
Model, Sequelize
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class MerchantTemp 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
}
};
MerchantTemp.init({
firstname: {
type: DataTypes.STRING,
allowNull: false
},
lastname: {
type: DataTypes.STRING,
allowNull: false
},
phone: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
ip_address: DataTypes.STRING,
}, {
sequelize,
modelName: 'merchant_temp',
});
return MerchantTemp;
};
validator.js
const { check, body, validationResult } = require('express-validator')
const bcrypt = require('bcrypt')
const models = require('../app/models')
const MerchantTemp = db.rest.models.MerchantTemp
const signupValidation = () => {
return [
body('firstname')
.not().isEmpty().trim().withMessage('Firstname field is required'),
body('lastname')
.not().isEmpty().trim().withMessage('Lastname field is required'),
body('phone')
.not().isEmpty().trim().withMessage('Phone Number field is required')
.isNumeric().withMessage('Phone Number field can only contain Numbers')
.isLength({min: 11, max: 13}).withMessage('Phone Number field can only contain minimum of 11 and max of 13 digits respectively'),
body('email')
.not().isEmpty().trim().withMessage('Email Address field is required')
.isEmail().withMessage('Email field is not a valid format').normalizeEmail()
.custom((value, { req }) => {
/**
** THIS PART IS WHERE I AM VALIDATING IF IT EXIST
**/
MerchantTemp.findOne({ where: { email: req.body.email } })
.then((result) => {
console.log(result)
}).catch(error => {
console.log(error)
})
}),
body('password')
.not().isEmpty().trim().withMessage('Password field is required')
.isStrongPassword(
{
minLength: 6,
minLowercase: 1,
minUppercase: 1,
minSymbols: 1
}).withMessage('Password is too weak. Field must contain min. of 6 characters, 1 lowercase and uppercase character and a symbol')
]
}
const validate = (req, res, next) => {
const errors = validationResult(req)
if (errors.isEmpty()) {
return next()
}
const extractedErrors = []
errors.array().map(err => extractedErrors.push({ msg: err.msg }))
res.status(200).json({
statusCode: 400,
errors: extractedErrors
})
}
module.exports = {
signupValidation,
validate
}
router file
require('dotenv').config()
const router = require('express').Router()
const account = require('../controllers/account.controller')
const { signupValidation, validate } = require('../../helpers/validator')
router.get('/', (req, res) => {
let p = "This serves as a repository of API calls for application"
res.status(200).json({message:p, statusCode: 200})
})
//Endpoint to create new merchant
router.post('/account/create-merchant', signupValidation(), validate, async (req, res) => {
res.status(200).json({
statusCode: 201,
message: req.body
})
})
//Endpoint to login merchant
router.post('/account/login', (req, res) => {
})
module.exports = router
The validation on the /account/create-merchant route works well prior to when I included the findOne part in the validator.js. What exactly I am doing wrong?

beforeBulkDestroy not finding model property to change

I am trying to use the beforeBulkDestory Sequelize hook on a user delete that will switch the deleted column boolean to true prior to updating the record to add a timestamp for deleted_at. However, when I console.log the function parameter it provides a list of options and not the model object that I can update for the record of focus. Am I approaching this the wrong way? Is this something that should be set using model instances?
API Call:
import db from '../../../models/index';
const User = db.users;
export default (req, res) => {
const {
query: { id },
} = req
console.log(User)
if (req.method === 'DELETE') {
User.destroy({
where: {
id: id
}
}).then(data => {
res.json({
message: 'Account successfully deleted!'
})
})
} else {
const GET = User.findOne({
where: {
id: id
}
});
GET.then(data => {
res.json(data)
})
}
}
Parameter Values (beforeBulkDestroy, afterBulkDestroy):
beforeBulkDestroy
{
where: { id: '5bff3820-3910-44f0-9ec1-e68263c0f61f' },
hooks: true,
individualHooks: false,
force: false,
cascade: false,
restartIdentity: false,
type: 'BULKDELETE',
model: users
}
afterDestroy
{
where: { id: '5bff3820-3910-44f0-9ec1-e68263c0f61f' },
hooks: true,
individualHooks: true,
force: false,
cascade: false,
restartIdentity: false,
type: 'BULKUPDATE',
model: users
}
Model (users.js):
'use strict';
const Sequelize = require('sequelize');
const { Model } = require('sequelize');
const bcrypt = require("bcrypt");
module.exports = (sequelize, DataTypes) => {
class users 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
}
};
users.init({
id: {
type: DataTypes.UUID,
defaultValue: Sequelize.UUIDV4,
primaryKey: true
},
first_name: DataTypes.STRING,
last_name: DataTypes.STRING,
password: {
type: DataTypes.STRING
},
email: DataTypes.STRING,
active: {
type: DataTypes.BOOLEAN,
defaultValue: true
},
deleted: {
type: DataTypes.BOOLEAN,
defaultValue: false
}
}, {
hooks: {
beforeDestroy: (user, options) => {
console.log("beforeDestroy")
console.log(user)
console.log(options)
user.deleted = true
}
},
sequelize,
freezeTableName: true,
modelName: 'users',
omitNull: true,
paranoid: true,
underscored: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
deletedAt: 'deleted_at',
hooks: {
beforeCreate: async function(user){
console.log("beforeCreate")
console.log(user)
const salt = await bcrypt.genSalt(12);
user.password = await bcrypt.hash(user.password, salt);
console.log(user.password)
},
beforeBulkDestroy: async function(user){
console.log("beforeBulkDestroy")
console.log(user)
},
afterBulkDestroy: async function(user){
console.log("afterDestroy")
console.log(user)
}
}
});
users.prototype.validPassword = async function(password) {
console.log("validatePassword")
console.log(password)
return await bcrypt.compare(password, this.password);
}
return users;
};
the before/after bulkDestroy hooks only receive the options, not the instances. One way you could do this is defining a before/after Destroy hook:
hooks: {
beforeDestroy: (user, { transaction }) => {
user.update({ deleted: true }, { transaction });
}
}
and calling User.destroy with the individualHooks option:
User.destroy({ where: { id: id }, individualHooks: true });
Be aware that this will load all selected models into memory.
Docs
Note: In your case, since you're only deleting one record by id, it would be better to just user = User.findByPk(id) then user.destroy(). This would always invoke the hooks and it also makes sure the record you want to delete actually exists.
Note 2: Not sure why you need a deleted column, you could just use deletedAt and coerce it into a boolean (with a virtual field if you want to get fancy).

Instance Methods is not working in Sequelize

I can't seem to set the User model properly. I've implemented some instance methods that don't seem to be working
var UserDetails = mysequelize.sequelize.define('user_tb', {
id: {
autoIncrement: true,
type: mysequelize.sequelize.Sequelize.INTEGER,
allowNull: false,
},
username: {
type: mysequelize.sequelize.Sequelize.STRING,
primaryKey: true,
unique: true,
},
hierarchyid: {
type: mysequelize.sequelize.Sequelize.STRING
},
password: {
type: mysequelize.sequelize.Sequelize.STRING,
validate: {
len: {
args: [6, 15],
msg: "Please enter a password with at least 6 chars but no more than 15"
}
},
allowNull: false
},
customerid: {
type: mysequelize.sequelize.Sequelize.INTEGER
},
statususer: {
type: mysequelize.sequelize.Sequelize.STRING,
allowNull: false
},
userid: {
unique: true,
type: mysequelize.sequelize.Sequelize.STRING,
allowNull: false
},
authtoken: {
unique: true,
type: mysequelize.sequelize.Sequelize.STRING,
allowNull: false
}
},
{
tableName: 'user_tb',
timestamps: false,
freezeTableName: true
});
UserDetails.prototype.toJSON = function(){
var user = this;
var userObject = user.toObject();
return _.pick(userObject,['userid','password']);
};
UserDetails.prototype.findByCredentials = function(userid, password)
{
console.log('Sunddep');
var User = this;
// return User.fin
User.findOne({userid}).then((user)=> {
if(!user)
{
return Promise.reject();
}
return new Promise((resolve,reject) => {
bcrypt.compare(password,user.password,(err,res) => {
if(res)
{
resolve(user);
}
else{
reject();
}
})
})
});
}
UserDetails.prototype.generateAuthToken = function()
{
var user = this;
var access = 'authtoken';
var token = jwt.sign({userid:
user.userid.toHexString(),access},process.env.JWT_SECRET).toString();
user.build({
access: token
});
user.save().then(() =>{
return token;
});
}
module.exports = {UserDetails}
server.js
app.post('/user/login', (req, res) => {
console.log(req.body);
var body = _.pick(req.body, ['userId', 'password']);
user.findByCredentials(body.userId, body.password).then(() => {
res.send('Sundeep');
},
(e) => {
sendData: ({
wsState: '0',
messageCode: 'WS000001',
message: 'No user find with this Id',
userData: []
});
res.status(400).send(sendData)
});
});
Hi, I am getting error while calling instance method from other class. Can any one tell me how can i achieve it
UserDetails.prototype.findByCredentials = function(userid, password) {.....}
But while run the server.js file i getting error like UserDetails.findByCredentials is not a function while calling from other class
Thanks for help in advance.

Resources