Soft delete using nodejs + mongodb - javascript

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)
}
})

Related

Check if document exists in collection with Mongoose Model

I want to to check if email already exists in 'users' collection:
I have this model:
const isEmailExists = async (value) => {
const res = await User.countDocuments({ email: value });
return res > 0;
}
const User = mongoose.model('User', {
email: {
type: String,
required: true,
validate(value) {
isEmailExists(value).then(res => {
if (res) {
throw new Error('Email already exists');
}
})
}
}
});
And I use post method with express router:
router
.route('/register')
.get((req, res) => {
res.sendFile(publicDirPath + '/auth/register.html');
})
.post(async (req, res) => {
const user = new User(req.body);
try {
const saveUser = await user.save();
res.send(saveUser);
} catch (error) {
res.send(error);
}
});
For some reason, it does not work and the user is been added anyway..
What am i doing wrong ?
If you want to check if one document with a certain entry/value exists you can do this :
function emailExists(value) {
User.findOne({email: value}).then((err, user) => !!user)
}

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" });
}
};

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)

How to use instance methods on sequelize

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);

Mongoose assign a collection to another

I am trying to add a post to a user collection after the user was created with empty posts. I have tried with populate with no success .. any help is much appreciated.
// Post Model
const mongoose = require('mongoose');
const { Schema } = mongoose;
const UserModel = require('./user-model');
let PostSchema = new Schema({
author: {
ref: 'users',
type: Schema.Types.ObjectId
},
content: String,
description: String,
date: {
default: new Date(),
type: Date
},
title: String,
updatedAt: {
default: new Date(),
type: Date
}
});
let PostModel = mongoose.model('posts', PostSchema);
module.exports = PostModel;
// User Model
const mongoose = require('mongoose');
const { Schema } = mongoose;
const PostModel = require('./post-model');
let UserSchema = new Schema({
name: {
type: String
},
email: {
lowercase: true,
type: String,
trim: true,
unique: true
},
password: {
type: String,
},
postList: [{
ref: 'posts',
type: Schema.Types.ObjectId
}],
});
const UserModel = mongoose.model('users', UserSchema);
module.exports = UserModel;
// save post controller
exports.savePost = (request, response, next) => {
let { author, description, title } = request.body;
let post = new PostModel({ author, description, title }).save();
UserModel.findById(author)
.then((user) => {
user.postList.push(post);
// Still Fails
// How can i assign the post to the user ?
});
}
Is there any way of doing this other then push or populate ?
To solve this problem I prefer to use $push of mongo
UserModel.findOneAndUpdate({
_id: author.id,
{
$push: {
postList: post
}
}
});
You need to follow this process to save successfully
Save post if success then
Update user to push postId in postlist
can try this one
exports.savePost = (request, response, next) => {
let post = new PostModel(request.body)
post.save(function(err, data) {
if(err) {
//return error
}
// check by console your author is present or not
// let author in your req.body
let author = req.body.author
UserModel.findOneAndUpdate({_id: author},{$push: {postList: post._id}},{new:true} function(error, user) {
if(error) {
// return error
}
console.log(user)
// return success
})
});
}
exports.savePost = (request, response, next) => {
let { user, description, title } = request.body;
let post = new PostModel({ user, description, title });
post.save()
.catch((error) => {
if (error)
throw new Error(error);
})
.then((post) => {
UserModel.findOneAndUpdate({ _id: user }, {$push: { postList: post._id } })
.populate('postList')
.catch((error) => {
if (error)
throw new Error(error);
})
.then((user) => {
user.postList.forEach((item, postion) => {
console.log(`${item} -at ${postion} \n`);
});
});
});
}
This is what i did and it worked after all. I don't know if this is the correct solution but this is working.

Categories