I am implementing a feature that will allow users to follow accounts and be followed, I made my schema like this
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('Follows', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
followerId: {
allowNull: false,
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'id'
},
onDelete: 'CASCADE',
},
followingId: {
allowNull: false,
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'id'
},
onDelete: 'CASCADE',
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Follows');
}
};
Association tables with the Users table
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Follow 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
models.Follow.belongsTo(models.User, {
as: 'follower',
foreignKey: 'followerId',
});
models.Follow.belongsTo(models.User, {
as: 'following',
foreignKey: 'followingId',
});
}
};
Follow.init({
followerId: DataTypes.INTEGER,
followingId: DataTypes.INTEGER,
}, {
sequelize,
modelName: 'Follow',
});
return Follow;
};
I don't know if I made a mistake in my associations
Controllers
const models = require('../models');
const { Follow, User } = models;
module.exports.follow = async (req, res) => {
const { id } = req.params
const { followerId, followingId } = req.body;
try {
const userFollowing = await User.findByPk(id);
if (userFollowing) {
if (!userFollowing) {
res.status(401).json("Cet utilisateur n'est pas disponible")
}
// console.log(userFollowing);
Follow.update({ followingId: followerId }, { where: { id } }).then((user) => {
if (user) {
userFollowing.update({
following: followerId
})
return res.status(201).json({ "Vous suivez maintenant": user.following })
}
else {
return res.status(400).json('Impossible de suivre cette personne 😢');
}
})
}
const userFollower = await User.findByPk(followingId);
if (userFollower) {
if (!userFollower) {
res.status(401).json("Cet utilisateur n'est pas disponible")
}
//console.log(userFollower);
Follow.update({ followerId: userFollower }, { where: { id } }).then((user) => {
if (user) {
userFollower.update({
followers: followingId
})
res.status(200).json({ "Vous êtes suivi maintenant par ": userFollower.fristname + userFollower.lastname })
}
else {
if (err) return res.status(400).json('Impossible de suivre cette personne 😢');
}
}).catch((err) => res.status(400).json(err))
}
} catch (err) {
return res.status(500).json("err" + err)
}
}
Here the request is well executed and sends a 201 response to update the element the problem is that the response is not added in the database
Related
Im trying to create a sequalized query that Searches For a Team Name & Filter To Sport Type but when I test the JSON body on Postman I get an error and its not returning the data. The JSON body matches the data that its meant to return but its not matching up.
Test on Postman
{
"searchTerm": "Kaizer fc",
"sportType": "2"
}
teams.js
const Teams = require('../models').teams;
const {sequelize, QueryTypes } = require('sequelize');
const db = require('../models')
const search = (searchTerm, sportType) => {
return new Promise(async (resolve, reject) => {
try {
console.log("Testing");
const teams = await db.sequelize.query(
`SELECT teams.name, institutions.name, addresses.line1, addresses.line2, addresses.country
FROM teams
INNER JOIN institutions
ON teams.institution_id = institutions.id
INNER JOIN addresses
ON institutions.address_id = addresses.id
WHERE teams.name like :search_name and teams.sport_type_id LIKE :sport_type`,
{
replacements: { search_name: searchTerm, sport_type: sportType },
type: QueryTypes.SELECT
}
);
return resolve(teams);
} catch (err) {
return reject(err)
}
})
}
teams.js - Models Folder
'use strict';
module.exports = (sequelize, DataTypes) => {
const teams = sequelize.define('teams', {
name: { type: DataTypes.STRING, allowNull: false },
media_id: { type: DataTypes.STRING, allowNull: true },
state_id: { type: DataTypes.INTEGER, allowNull: true },
status_id: { type: DataTypes.INTEGER, allowNull: true },
sport_type_id: { type: DataTypes.INTEGER, allowNull: false },
created_by: { type: DataTypes.INTEGER, allowNull: true, references: { model: 'users', key: 'id' } },
modified_by: { type: DataTypes.INTEGER, allowNull: true, references: { model: 'users', key: 'id' } },
primary_user_id: { type: DataTypes.INTEGER, allowNull: true, references: { model: 'users', key: 'id' } },
institution_id: { type: DataTypes.INTEGER, allowNull: true, references: { model: 'institutions', key: 'id' } },
}, {
createdAt: 'created_at',
updatedAt: 'updated_at',
indexes: [
{
unique: true,
fields: ['id']
}
],
});
return teams;
};
search.js - Controllers
const helper = require('../utils/helper');
const teamsService = require('../services/teams');
const search = async (req, res) => {
try {
const data = await teamsService.search(req.body);
console.log("TESTING");
console.log(data);
return res.send(data);
} catch (err) {
return helper.handleError(err, req, res);
}
}
module.exports = search;
search function has two arguments: searchTerm and sportType and you pass the whole req.body as a first argument so that's why it becomes a value for searchTerm and you got this error about the whole value from Sequelize.
Just extract both props from req.body OR define search with props passed by as an object:
const { searchTerm, sportType } = req.body;
const data = await teamsService.search(searchTerm, sportType);
OR
const data = await teamsService.search(req.body);
...
const search = ({ searchTerm, sportType }) => {
return new Promise(async (resolve, reject) => {
I'm trying to learn Sequelize.js and I'm confused about its Many-To-Many Association.
What I've tried to do is simple Tasks Management with Users. Each task can be assignable to other users.
So, there's Users, Tasks and TaskContributors tables.
First, I made the POC version of my idea with Express.js. Then I found that I don't know how to insert to the Relational Table within different router.
Here are the model codes.
User Model
'use strict'
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define(
'User',
{
uid: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4 },
name: DataTypes.STRING,
password: DataTypes.STRING,
},
{},
)
User.associate = function (models) {
User.belongsToMany(models.Task, {
as: 'Contributors',
through: 'TaskContributors',
foreignKey: 'userId',
})
}
return User
}
Task Model
'use strict'
module.exports = (sequelize, DataTypes) => {
const Task = sequelize.define(
'Task',
{
uid: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4 },
name: DataTypes.STRING,
description: DataTypes.TEXT,
status: DataTypes.BOOLEAN,
},
{},
)
Task.associate = function (models) {
Task.belongsToMany(models.User, {
as: 'Task',
through: 'TaskContributors',
foreignKey: 'taskId',
})
}
return Task
}
TaskContributor Model
'use strict'
module.exports = (sequelize, DataTypes) => {
const TaskContributor = sequelize.define(
'TaskContributor',
{
userId: {
allowNull: false,
type: DataTypes.INTEGER,
references: { model: 'Users', key: 'id' },
},
taskId: {
allowNull: false,
type: DataTypes.INTEGER,
references: { model: 'Tasks', key: 'id' },
},
userStatus: { allowNull: false, type: DataTypes.STRING },
},
{},
)
TaskContributor.associate = function (models) {}
return TaskContributor
}
Routers
Users Router
router.get('/create/:name', (req, res) => {
User.create({ name: req.params.name, password: '123' })
.then((result) => res.send(result))
.catch((err) => res.status(500).send(err))
})
Tasks Router
router.get('/create/:userId/:name', (req, res) => {
const { userId, name } = req.params
User.findOne({ where: { uid: userId } }).then(async (user) => {
console.log(user)
const maybeTask = await user
.addTask({
name,
description: '',
status: false,
through: { userStatus: 'owner' },
})
.catch((err) => err)
if (maybeTask instanceof Error) res.status(500).send(maybeTask)
res.send(maybeTask)
})
})
When I tried to create new Task, it said user.addTask is not a function.
From what I understand from the Docs is that they showed how to create M-M association with two model.create() Objects, but not with the scenario like creating in different file.
I have a scenario in which if you like a post, it will change
liked:false to liked:true
This liked is based if the current user liked the post. The problem is when a new user signs up, it will still show liked being true despite the new user NOT liking the post.
How would i be able to check if the current user liked the post ? I don't think my logic is somewhat right as far as checking if the current user liked the post.
I want to keep the findAll functionality, i should get all posts, not just by the current user.
Sorta like instagram, or facebook.
this is posts array
and this is how im liking a post
likePost
likePost: async (req: any, res: Response) => {
const created = await models.Likes.findOne({
where: {
userId: req.session.user.id,
resourceId: req.params.id
}
});
console.log(created);
const post = await models.Post.findOne({ where: { id: req.params.id } });
// if like not created then do this
if (!created && post) {
await models.Likes.create({
userId: req.session.user.id,
resourceId: req.params.id
}).then(() => {
post.increment("likeCounts", { by: 1 });
post.update({ liked: req.session.user.id ? true : false });
res.status(200).send({
message: "You liked this post"
});
});
// else if post does not exist
} else if (!post) {
res.status(200).send({
message: "there is not post to be liked"
});
} else {
// else if a like does exist destroy like
await models.Likes.destroy({
where: {
userId: req.session.user.id
}
}).then(() => {
post.decrement("likeCounts", { by: 1 });
res.status(200).send({
message: "You unliked this post"
});
});
}
this is how im getting the posts.
getPosts
getPosts: async (req: any, res: Response) => {
await models.Post.findAll({
include: [
{ model: models.User, as: "author", attributes: ["username"] },
{ model: models.Likes }
],
order: [["createdAt", "DESC"]],
limit: 6
}).then(posts => {
res.json(posts);
});
},
Post.js(model)
"use strict";
module.exports = (sequelize, DataTypes) => {
var Post = sequelize.define("Post", {
title: DataTypes.STRING,
postContent: DataTypes.STRING,
liked: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
},
likeCounts: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
validate: {
min: 0,
}
},
authorId: DataTypes.INTEGER
}, {});
Post.associate = function (models) {
Post.belongsTo(models.User, {
as: "author",
foreignKey: "authorId",
onDelete: "CASCADE"
});
Post.hasMany(models.Likes, {
foreignKey: "resourceId",
timestamps: false,
targetKey: "id",
onDelete: "CASCADE"
});
};
return Post;
};
I believe the bug you are seeing is because you are not resolving the promises that are returned by:
post.increment("likeCounts", { by: 1 });
post.update({ liked: req.session.user.id ? true : false });
This means that the response will be send before those queries execute. The post.liked value will be set to true any time there is a user.id on the session. You may want to consider using transactions to roll back some of the earlier queries if later ones fail. I would also recommend using Promise.all() to make concurrent queries (it will be faster) and use async/await exclusively without mixing in thenables.
likePost: async (req: any, res: Response) => {
// fetch created and post at the same time
const [ created, post ] = await Promise.all([
models.Likes.findOne({
where: {
userId: req.session.user.id,
resourceId: req.params.id
}
}),
models.Post.findOne({
where: {
id: req.params.id
}
}),
]);
// no post, no updates
if (!post) {
return res.status(200).send({
message: "there is no post to be liked"
});
}
// we are going to make updates, so use a transaction, you will need to reference sequelize
let transaction;
try {
transaction = await sequelize.transaction();
if (!created && post) {
// use Promise.all() for concurrency
await Promise.all([
models.Likes.create({
userId: req.session.user.id,
resourceId: req.params.id
}, { transaction }),
post.increment("likeCounts", { by: 1, transaction }),
post.update({ liked: req.session.user.id ? true : false }, { transaction }),
]);
await transaction.commit();
return res.status(200).send({
message: "You liked this post"
});
}
await Promise.all([
models.Likes.destroy({
where: {
userId: req.session.user.id
}
}, { transaction }),
post.decrement("likeCounts", { by: 1, transaction }),
]);
await transaction.commit();
return res.status(200).send({
message: "You unliked this post"
});
} catch (err) {
if (transaction) {
await transaction.rollback();
}
console.log('There was an error', err);
return res.status(500);
}
}
To only return Likes for the current user on the getPost()
getPosts: async (req: any, res: Response) => {
const posts = await models.Post.findAll({
include: [
{ model: models.User, as: "author", attributes: ["username"] },
// limit the likes based on the logged in user
{ model: models.Likes, required: false,
where: { userId: req.session.user.id },
},
],
order: [["createdAt", "DESC"]],
limit: 6
});
return res.json(posts);
},
So by following #doublesharp help, i was able to determine if the current user liked the post or not, by using a sequelize data type VIRTUAL, along with using getDataValue
updated code
Post(model)
"use strict";
module.exports = (sequelize, DataTypes) => {
var Post = sequelize.define("Post", {
title: DataTypes.STRING,
postContent: DataTypes.STRING,
liked: {
type: DataTypes.VIRTUAL,
allowNull: false,
defaultValue: false,
get: function () {
return this.getDataValue('Likes').length ? true : false;
}
},
likeCounts: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
validate: {
min: 0,
}
},
authorId: DataTypes.INTEGER
}, {});
Post.associate = function (models) {
Post.belongsTo(models.User, {
as: "author",
foreignKey: "authorId",
onDelete: "CASCADE"
});
Post.hasMany(models.Likes, {
foreignKey: "resourceId",
timestamps: false,
targetKey: "id",
onDelete: "CASCADE"
});
};
return Post;
};
//# sourceMappingURL=post.js.map
I have a problem with my code when using sequelize auto.
the problem is how to connect sequelize model to database setting on DB.js, so that we can using findall function or any else
Model :
/* jshint indent: 2 */
const db = require('../../db/database')
const sequelize = db.sequelize
module.exports = function(sequelize, DataTypes) {
cust: sequelize.define('ms_customer', {
id: {
type: DataTypes.CHAR(10),
allowNull: false,
primaryKey: true
},
gender_id: {
type: DataTypes.INTEGER(1),
allowNull: true
},
status: {
type: DataTypes.CHAR(1),
allowNull: true
}
}, {
tableName: 'ms_customer'
});
};
DB.js connection :
const Sequelize = require('sequelize')
const sqlz = new Sequelize('db', 'user', 'pass', {
host: 'localhost',
dialect: 'mysql',
pool: {
max: 5,
min: 10,
idle: 10000
}
})
sqlz
.authenticate()
.then(() => {
console.log('Connection has been established successfully.');
})
.catch(err => {
console.error('Unable to connect to the database:', err);
});
module.exports = {
sqlz: sqlz,
Sequelize: Sequelize
}
Query :
const customerModel = require('../../db/model/ms_customer')
const cust = customerModel.cust
module.exports = {
modelGetCustomerById : (param,req,res) => {
cust.findAll({
where: {
id: {
param
}
}
}).then(customer => {
// console.log(customer)
res.json(customer)
})
}
}
Controller :
const customer = require('../../db/query/customer')
const sequelize = require('sequelize')
module.exports = {
getCustomerById: async(req,res) => {
var customerId = req.params.id
let getCustomerId = await customer.modelGetCustomerById(customerId)
// console.log(getCustomerId)
if(!getCustomerId){
return res.status(500).json({status: "Failed", message:"User not found"});
}
return res.status(200).json({status: "Success", message:getCustomerId});
}
}
why i always got error
Cannot read property 'findAll' of undefined
please help..
I've running code without error on my project that remodel by sequelize-auto and connect by sequelize. Check it out :
on models/index.js: ( for connection to db )
'use strict';
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 = sequelize['import'](path.join(__dirname, file));
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
On models/m_bank.js code :
module.exports = function(sequelize, DataTypes) {
return sequelize.define('m_bank', {
bank: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: '',
primaryKey: true
},
bank_name: {
type: DataTypes.STRING,
allowNull: true
},
address: {
type: DataTypes.STRING,
allowNull: true
},
branch: {
type: DataTypes.STRING,
allowNull: true
},
create_date: {
type: DataTypes.DATE,
allowNull: true
},
update_date: {
type: DataTypes.DATE,
allowNull: true
}
}, {
tableName: 'm_bank',
timestamps: false,
schema: 'db_hrms'
});
};
on controllers/m_bank.js
const M_Bank = require('../models').m_bank;
module.exports = {
list(req, res) {
return M_Bank
.findAll()
.then(M_Bank => res.status(200).send(M_Bank))
.catch((error) => {
console.log(error.toString());
res.status(400).send(error)
});
},
getById(req, res) {
return M_Bank
.findById(req.params.id)
.then(M_Bank => {
if (!M_Bank) {
return res.status(404).send({ message: "M_Bank Not Found" });
}
return res.status(200).send(M_Bank);
})
.catch((error) => res.status(400).send(error));
},
add(req, res) {
return M_Bank
.findOrCreate({
where: { bank: req.body.bank },
defaults: {
bank: req.body.bank,
bank_name: req.body.bank_name,
address: req.body.address,
branch: req.body.branch
}
})
.spread((M_Bank, created) => {
return res.status(created === false ? 400 : 200).send({
messsage: "Bank record " + (created === false ? "Already exists" : "successfully added")
})
})
// .then((M_Bank) => { res.status(201).send(M_Bank) })
// .catch((error) => res.status(400).send(error));
},
update(req, res) {
return M_Bank
.findById(req.params.id)
.then(M_Bank => {
if (!M_Bank) {
return res.status(404).send({ message: "Bank Not Found" });
}
return M_Bank
.update({
bank: req.body.bank || M_Bank.bank,
bank_name: req.body.bank_name || M_Bank.bank_name,
address: req.body.address || M_Bank.address,
branch: req.body.branch || M_Bank.branch
})
.then(() => res.status(200).send({
message: "Bank successfully update"
}))
.catch((error) => res.status(400).send({
message: "Bank Not Found"
}));
})
.catch((error) => res.status(400).send({
message: "No parameter for Bank ID"
}));
},
delete(req, res) {
return M_Bank
.findById(req.params.id)
.then((M_Bank) => {
if (!M_Bank) {
return res.status(404).send({ message: "M_Bank Not Found" });
}
return M_Bank
.destroy()
.then(() => res.status(204).send({
message: "Bank record successfully delete"
}))
.catch((error) => res.status(400).send({
message: "Bank Not Found"
}));
})
.catch((error) => res.status(400).send(error));
}
}
on config/config.json configuration code:
{
"development": {
"username": "tihagi",
"password": "123456",
"database": "db_tihagi",
"host": "127.0.0.1",
"dialect": "postgres"
},
"test": {
"username": "tihagi",
"password": "123456",
"database": "db_tihagi",
"host": "127.0.0.1",
"dialect": "postgres"
},
"production": {
"username": "tihagi",
"password": "123456",
"database": "db_tihagi",
"host": "127.0.0.1",
"dialect": "postgres"
}
}
Hope this can help you.
Note: my db is postgres, please change as per your dialect db.
-Tihagi
Possible reason could be that you didn't pass the model name correctly. What is the output of console.log(cust)
Change your customer model to this one.
/* jshint indent: 2 */
const db = require('../../db/database')
const sequelize = db.sequelize
module.exports = function(sequelize, DataTypes) {
var cust = sequelize.define('ms_customer', {
id: {
type: DataTypes.CHAR(10),
allowNull: false,
primaryKey: true
},
gender_id: {
type: DataTypes.INTEGER(1),
allowNull: true
},
status: {
type: DataTypes.CHAR(1),
allowNull: true
}
}, {
tableName: 'ms_customer'
});
return cust;
};
Your query to
const customerModel = require('../../db/model/ms_customer')
//const cust = customerModel.cust .... you do not need this.
module.exports = {
modelGetCustomerById : (param,req,res) => {
customerModel.findAll({
where: {
id: {
param
}
}
}).then(customer => {
// console.log(customer)
res.json(customer)
})
}
}
I am trying to upgrade Sequelize V3 to V4.
Followed the breaking changes instructions here.
I get an error when running the following query with V4:
Auth.findOne({ . // This is where it failes
include: {
required: true,
include: {
where: {
id: decoded.id,
token: token,
}
}
}
})
Setup:
Existing Code with V3 (works):
module.exports = function (sequelize, DataTypes) {
const Auth = sequelize.define('Auth', {
token: {
type: DataTypes.STRING,
allowedNull: true,
unique: true,
required: true,
validate: {
len: [5, 200]
}
}
device: {
type: DataTypes.STRING,
allowedNull: true,
unique: false,
}
}, {
tableName: 'Auth',
classMethods: {
associate: function (models) {
Auth.belongsTo(models.User, {
foreignKey: {
name: 'user_id',
allowedNull: false,
}
})
Auth.hasMany(models.Endpoint, {
as: 'endpoints',
foreignKey: 'auth_id'
})
},
findByToken: function (token) {
var User = this
var decoded
try {
decoded = jwt.verify(token, 'abc123')
} catch (e) {
return Promise.reject()
}
return Auth.findOne({
where: {
id: decoded.id,
token: token,
}
})
}
}, instanceMethods: {
generateAuthToken: function () {
var auth = this
var access = 'auth'
var token = jwt.sign({ id: auth.id, access }, 'abc123').toString()
auth.token = token
auth.code = null
auth.save().then(() => {
})
}
}
})
return Auth
}
Upgrading to V4 (TypeError occurs)
module.exports = function (sequelize, DataTypes) {
var Auth = sequelize.define('Auth', {
token: {
type: DataTypes.STRING,
allowedNull: true,
unique: true,
required: true,
validate: {
len: [5, 200]
}
},
device: {
type: DataTypes.STRING,
allowedNull: true,
unique: false,
}
})
Auth.associate = function (models) {
Auth.belongsTo(models.User, {
foreignKey: {
name: 'user_id',
allowedNull: false,
}
})
Auth.hasMany(models.Endpoint, {
as: 'endpoints',
foreignKey: 'auth_id'
})
}
Auth.findByToken = function (token) {
var User = this
var decoded
try {
decoded = jwt.verify(token, 'abc123')
} catch (e) {
return Promise.reject()
}
return Auth.findOne({ . // This is where it fails
include: {
required: true,
include: {
where: {
id: decoded.id,
token: token,
}
}
}
})
}
Auth.prototype.generateAuthToken = function () {
var auth = this
var access = 'auth'
var token = jwt.sign({ id: auth.id, access }, 'abc123').toString()
auth.token = token
auth.code = null
auth.save().then(() => {
})
}
return Auth
}
Middleware
var authenticate = (req, res, next) => {
var token = req.header('x-auth')
var db = req.app.get('db')
db.Auth.findByToken(token).then((auth) => {
if (!auth) {
return Promise.reject()
}
req.user_id = auth.user_id
req.device_id = auth.device_id
req.auth_id = auth.id
return next()
}).catch((e) => {
return res.status(401).send()
// I get an error here: `TypeError: Cannot read property '_expandIncludeAll' of undefined
})
}
What am I doing wrong?
Have you tried without the "include" ?
Something like:
return Auth.findOne({ . // This is where it fails
where: {
id: decoded.id,
token: token,
}
});
should work.
"include" is used for subqueries through associations but your where clause seems to be only on the Auth attributes (http://docs.sequelizejs.com/class/lib/model.js~Model.html#static-method-findAll)
You'd use "include" if you wanted to filter on some attributes on User.
You've to do include : { model : YourModel }.
Doing include: { include : Model} throws this exact error.