Mongoose: Whats wrong w/ my findByIdAndUpdate? - javascript

my idea is very simple, it is get the update value from req.body, but it doenst work properly, the data never change in the mongodb.
already tried the {$set: email, password}
const id = { _id: req.params.id };
const { email, password } = req.body;
let user = await User.findByIdAndUpdate(id, { email, password });
if (!user) {
return res.json({ message: "error"});
}
return res.json({
user: user,
updated: true
})

you passing a object into to id field when all it needs is a string/ObjectId
const { email, password } = req.body;
let user = await User.findByIdAndUpdate(req.params.id, { email, password });
if (!user) {
return res.json({ message: "error"});
}
return res.json({
user: user,
updated: true
})

The Expression :
const id = { _id: req.params.id };
creates an Object named id with a field _id set to the actual id value from the params.
So eventually what you end up doing is passing an entire object instead of an id in the first argument to findByIdAndUpdate which only accepts an id as the first parameter.
If you still want to use the object just replace id with id._id in the method findByIdAndUpdate. Which you would then write as :
let user = await User.findByIdAndUpdate(id._id, { email, password });

Related

how to hide my client's password in API response

I am just a beginner at Javascript & MERN. I am trying to create a small social media app, and in my sign up api, I gave a response of the user's info. I couldn't segregate and hide the password.
here is the code
userRouter.post("/signUp", async (req, res) => {
const {name, userName, email, password} = req.body
const existingUser = await userSchema.findOne({email: email})
const SameUserName = await userSchema.findOne({userName: userName})
if (existingUser) {
return res.status(406).send({
message: `sorry, an account with email: ${email} has already been created.`
})
} else if (SameUserName) {
return res.status(406).send({
message: `sorry, user name taken. Try another one...`
})
}
const newUser = new userSchema({
name,
userName,
email,
password
})
console.log(newUser)
try {
await newUser.save()
res.status(201).send({
message: `Account successfully created!`,
user: newUser
})
} catch (err) {
res.send({
message:`Something went wrong`,
})
}
})
So, how can I send the user info without the password?
Following up on the comment I left below, here is what you can do.
Refactoring of your code is must thou.
try {
const userSaved = await newUser.save();
delete userSaved.password // assuming this is the property name
return res.status(201).send({ message: 'Account created successfully', user: userSaved })
}
you could also just:
try {
const userSaved = await newUser.save();
delete userSaved.password // assuming this is the property name
return userSaved;
}
In this case you handle the message and everything on the front-end.
You'll want to implement the toJSON and transform methods on your schema. This will allow you to 'intercept' schema objects as they are created, and as they are serialized and sent to the client.
Here's an example:
Schema:
import { Schema, model } from 'mongoose';
const schema = new Schema(
{
name: {
required: true,
type: String
},
userName: {
required: true,
type: String
},
email: {
required: true,
type: String
},
password: {
required: true,
type: String
}
},
{
// here, we implement the `toJSON` method to serialize the user object sans password, __v;
// we'll also convert the mongo-specific `_id` property to a db-agnostic format
toJSON: {
transform(_, ret) {
ret.id = ret._id;
delete ret.password;
delete ret._id;
delete ret.__v;
}
}
}
);
// this is our user schema, used to initialize new user objects before we persist them in the db
const User = model('User', schema);
userRouter.post('/signUp', async (req, res) => {
// grab the inputs - we do *not* at this time know whether any of these are valid - they must be validated
const { name, userName, email, password } = req.body;
// validate the email format, performing checks for any requirements you wish to enforce
if (!email) {
// error response
}
// now, we check if the email is already in-use
const existingUser = await User.findOne({ email });
if (existingUser) {
return res.status(400).send({
message: `sorry, an account with email: ${email} has already been created.`
});
}
// validate userName format here
if (!userName) {
// error response
}
// notice we don't bother making this query until `existingUser` check has passed
// this way we don't incur needless computation
const sameUserName = await User.findOne({ userName });
if (sameUserName) {
return res.status(400).send({
message: `sorry, user name taken. Try another one...`
});
}
// validate name and password and handle accordingly here
if (!name || ...) {
// error response
}
// assuming all is well, we create a new user with the schema
// think of the schema as a template
const newUser = new User({ name, userName, email, password });
// save the new user
await newUser.save().catch((ex) => {
// error response
});
res.status(201).send({
message: `Account successfully created!`,
user: newUser
});
});
You might also look into express-validator, a middleware that handles much of the request body validation for you.

Mongoose Throw Error When Non Existing Key is Present

I have a code where I am updating my schema object with request body. I have applied validation rules on the schema. The problem is, I want the schema to throw an error when there's a non existing field in the request body. Non existing key doesn't save to the database as I want but I want to throw some error instead of saving the object. Schema:
const peopleSchema = new mongoose.Schema(
{
fullname: {
type: String,
required: [true, "fullname is required"],
validate: [(value) => isAlpha(value, "en-US", {ignore: " "}), "name should be alphabetic only"],
},
phone: {
type: String,
validate: [isPhone, "please enter a valid phone number"],
},
address: String,
},
{ timestamps: true }
);
Code to update person:
router.put("/:id", checkUser, async (req, res, next) => {
try {
const { id } = req.params;
const user = req.currentUser;
const person = user.people.id(id);
person.set(req.body);
const response = await user.save();
res.json({ response });
} catch (err) {
next(new BadRequestError(err));
}
});
for validation there are two way based on callback and async approache ,
because your code is based on async/await you must to use validateSync() like the following code:
let errors = user.validateSync()//check validation
if(errors){
console.log(errors)
throw errors;//handle your error
}
const response = await user.save()
in callback method :
user.save(function(err,response){
if (err){
console.log(err);
//handle error
}
else{
console.log(response)
res.json({ response });
}
})

select action occurs befor insert postgresql

im dealing with a problem i can't solve. I've got 2 tables - 'users' and 'login'. The 'users' table keep all my user's info and the 'login' table keep the user's emails and hashes:
this is my backend code:
const handleRegister = (req, res, db, bcrypt, saltRounds) => {
const { email, first_name, last_name, password } = req.body;
// creating hash for password
const salt = bcrypt.genSaltSync(saltRounds);
const hash = bcrypt.hashSync(password, salt);
// form validation
if ( !email || !first_name || !last_name || !password) {
return res.status(400).json('incorrect form submission');
}
// updating login and users tables in the database
db.transaction(trx => {
trx.insert({
first_name: first_name,
last_name: last_name,
email: email,
joined: new Date()
})
.into('users')
.returning('email')
.then(loginEmail => {
return trx('login')
.returning('email')
.insert ({
hash: hash,
email: loginEmail[0]
})
.then(userEmail => {
db.select('*').from('users').where('email', userEmail[0])
.then(user => {
userInfo = Object.assign(user[0], {lists: []} , {tasks: []});
res.json({user: userInfo});
})
.catch(err => {res.status(400).json('unable to get user')})
})
})
.then(trx.commit)
.catch(trx.rollback)
})
.catch(err => {
res.status(400).json('unable to register');
console.log(err);
})
At first when i try to add new user through postman everything is ok, but when i try to add another user i got an error: "TypeError: Cannot convert undefined or null to object" because for some reason the db.select in line 28 does not get any result and passes user=[]. The thing is that when i check my database - the user that i just added is there. it looks like its doing the db.select in line 28 before the insert in line 23...
thank you for your help.

Query resolver inside Mutation await function omitted

I'm having a little trouble when querying inside a mutation, I believe maybe I'm not calling correctly the query, because it executes but is not waiting for the response so I get an undefined value, please correct me.
Please note that I'm using prisma-binding
This is my mutation resolvers:
const Mutation = {
async signUp(parent, args, ctx, info) {
const password = await bcrypt.hash(args.password, 10)
const user = await ctx.db.mutation.createUser({ data: {...args, password} })
const token = jwt.sign({ userId: user.id }, process.env.PRISMA_SECRET)
return {
token,
user,
}
},
async login(parent, args, ctx, info) {
const user = await ctx.db.query.users( {where:{email: args.email}}, info)
if (!user) {
throw new Error('No such user found')
}
const valid = bcrypt.compare(args.password, user.password)
if (!valid) {
throw new Error('Invalid password')
}
const token = jwt.sign({ userId: user.id }, process.env.PRISMA_SECRET)
return {
token,
user,
}
},
};
module.exports = Mutation;
In the login function when querying the user or users I try both queries even knowing I have email as unique field it always print No such user found due I receive undefined, IDK if it's because prisma-binding or not doing correctly the function call to prisma.
Here is my schema.graphql
type Query {
// some queries
}
type Mutation {
signUp(
name: String!
email: String!
password: String!
): AuthPayLoad
login(
email: String!
password: String!
): AuthPayLoad
}
type AuthPayLoad {
token: String
user: User
}
So for prisma-binding I don't have to define the query users(), the binding will handle that right?? Even if was that, I would have another error which isn't the case.
Maybe I'm missing some little detail, will be grateful if someone point me to the right direction.
Thanks in advance...
You are passing the wrong selection set to the query. You are returning AuthPayload in login mutation but you are passing it to the user query which is surely incompatible.
try this
async login(parent, args, ctx, info) {
const user = await ctx.db.query.user( {where:{email: args.email}},`{ id name email }`) // pass in the other fields you want in this selection set
if (!user) {
throw new Error('No such user found')
}
const valid = bcrypt.compare(args.password, user.password)
if (!valid) {
throw new Error('Invalid password')
}
const token = jwt.sign({ userId: user.id }, process.env.PRISMA_SECRET)
return {
token,
user,
}
},

How to avoid setting fields to empty strings if value exists

I'm working on my first node.js application and I need some help.
I use MongoDb as database.
In my application I have created a (sign up) method that reads user input such as email & password and sets the other fields like first-name & last-name to empty strings.
exports.postSignup = (request, response, next) => {
const email = request.body.email;
const password = request.body.password;
const fonfirmPassword = request.body.confirmPassword;
User.findOne({ email: email })
.then(userDoc => {
if (userDoc) {
request.flash('error', 'Email already exists, please pick another!')
return response.redirect('/auth/signup');
}
return bcrypt.hash(password, 12)
.then(hashedPassword => {
const user = new User({
firstName: '',
lastName: '',
email: email,
photoUrl: '',
password: hashedPassword,
cart: { items: [] }
})
return user.save();
})
.then(result => {
response.redirect('/auth/login');
const signup = {
to: email,
from: 'support#company.com',
templateId: keys.SIGNUP_TEMPLATE_ID,
dynamic_template_data: {
subject: 'Signup succeeded successfully!',
},
};
sgMail.send(signup);
})
.catch(err => {
console.log(err);
});
})
.catch(err => {
console.log(err);
});
};
The code above works fine...
After a User has logged in to their account, that user is able to navigate to their profile page and set their first-name and last-name just as shown in the attached image.
enter image description here
So I have created another method that allows a User to set their first-name, last-name and photo-Url
exports.postAddProfile = (request, response, next) => {
const firstName = request.body.firstName;
const lastName = request.body.lastName;
const photoUrl = request.body.photoUrl;
User.findOne({ userId: request.user.userId })
.then(user => {
user.firstName = firstName;
user.lastName = lastName;
user.photoUrl = photoUrl;
return user.save();
})
.then(result => {
console.log('Added Profile Info');
response.redirect('/');
})
.catch(err => {
console.log(err)
});
};
This code also works fine But the issue is if a User sets their first-name, last-name and photo-Url the first time like (Jonas, Jsk and https://photourl.com)
Then the second time if a User only changes the first-name then last-name and photo-Url are again set to empty strings.
How can I avoid that?
Empty strings are falsies in JS, so just check if the response has a value that's not a empty string:
User.findOne({ userId: request.user.userId })
.then(user => {
user.firstName = firstName ? firstName : user.firstName;
user.lastName = lastName ? lastName : user.lastName;
user.photoUrl = photoUrl ? photoUrl : user.photoUrl;
return user.save();
})

Categories

Resources