How to check if username already exist in database collection MongoDB - node.js

I'm making post request on registration,But I want error to pop up if username is already taken.
Any suggestions?
Here is my post route:
app.post('/addUser', (req,res) => {
const addUser = new User({username: req.body.username, password: req.body.password})
addUser.save().then(result => res.status(200).json(result)).catch((err) => console.log(err))
})

Alternate method, depending on the error style you want.
const users = new mongoose.Schema(
{
username: {type: String, unique: 'That username is already taken'}
},
{ timestamps: true }
)
Now mongo will index usernames and check it before the insertion. An error will be thrown if it's not unique.

You can use findOne method of mongoose
app.post('/addUser', async (req,res) => {
//validation
var { username, password } = req.body;
//checking username exists
const existUsername = await User.findOne({ username: req.body.username});
if (existUsername) {
console.log('username taken');
}
});

Related

How to exclude password from user object sent in response in express.js?

I am returning a response object with the user and jwt token whenever a user logs in. But I want to exclude the password field when the user is sent in the response.
Controller:
const loginUser = asyncHandler(async (req, res) => {
const { email, password } = req.body;
// Check for user email
const user = await User.findOne({ email });
if (user && (await bcrypt.compare(password, user.password))) {
res.json({
user: user,
token: generateToken(user._id),
});
} else {
res.status(400);
throw new Error("Invalid credentials");
}
});
if I exclude the password when finding the user like this:
const user = await User.findById(decoded.id).select("-password");
Then bcrypt's compare method will not work as it needs user.password.
Please tell me how I can exclude password from user object in JSON response?
You can set undefined to the user before returning the user.
const loginUser = asyncHandler(async (req, res) => {
const { email, password } = req.body;
// Check for user email
const user = await User.findOne({ email });
if (user && (await bcrypt.compare(password, user.password))) {
user.password = undefined;
res.json({
user: user,
token: generateToken(user._id),
});
} else {
res.status(400);
throw new Error('Invalid credentials');
}
});
Or you can use the toJSON method in the user schema to exclude password:
const userSchema = new mongoose.Schema(
{
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
},
{
toJSON: {
transform(doc, ret) {
delete ret.password;
},
},
},
);

Stuck on NodeJS mongoDB find() Query

I am unable to write the correct query.
I am trying to check if the user already exists in the database and it will respond in Login Successfully Response.
This code is in working position problem lies in Query.
I hope somebody will help
function login() {
app.post("/login/", async(req, res) => {
const query = new Model({
email: req.body.email,
password: req.body.password,
});
const cursor = Model.find(query); // (*Here's the problem*)
console.log(cursor);
if (query === cursor) {**strong text**
console.log(query);
res.send("login successfully");
} else {
console.log(query);
res.send("user does not exist ");
}
});
}
login();
// Model and Schema
const LoginSchema = new mongoose.Schema({
email: { type: String, required: true },
password: { type: String, required: true },
});
const Model = mongoose.model("login_details", LoginSchema);
// Registeration Phase
function registration() {
app.post("/register/", async(req, res) => {
const model = new Model({
email: req.body.email,
password: req.body.password,
});
const result = await model.save();
console.log(result);
res.send(model);
});
}
// Headers
const express = require("express");
const app = express();
const mongoose = require("mongoose");
app.use(express.json());
app.use(express.urlencoded({ extend: true }));
//
Issues with your code
1- You don't need to use new Model({}) to query
2- You're using .find() which returns an array, use findOne() instead
3- You're attempting to check if a mongoose model (with _id) equals a query without the _id which won't work
4- Use return at the end of your function (won't affect the functionality here but just as good practice not to encounter errors like cannot set headers after they're sent)
possible solution
function login() {
app.post("/login/", async (req, res) => {
const cursor = await Model.findOne({
email: req.body.email,
password: req.body.password,
});
console.log(cursor);
if (cursor) {
console.log(cursor);
return res.send("login successfully");
} else {
return res.send("user does not exist ");
}
});
}
You are using Mongoose in the wrong way. Try this.
const result = await Model.find({
email: req.body.email,
password: req.body.password,
});

{select: false} works differently on mongoose queries

I am trying to create a user in mongoose and return it after User.create query without password field. I set "select: false" on password field in model schema but it keeps returning me password in response after User.create.
// models/user.js
const userSchema = new mongoose.Schema({
// ...
password: {
type: String,
required: true,
minlength: 5,
select: false,
},
});
// routes/index.js
routes.post(
"/sign-up",
celebrate({
body: Joi.object().keys({
name: Joi.string().min(2).max(30),
about: Joi.string().min(2).max(30),
avatar: Joi.string().pattern(RegExp(urlChecker)),
email: Joi.string().required().email(),
password: Joi.string().required().min(5),
}),
}),
usersController.createUser,
);
// controllers/user.js
const User = require("../models/user");
exports.createUser = (req, res, next) => {
const {
name,
about,
avatar,
email,
password,
} = req.body;
bcrypt
.hash(password, 10)
.then((hash) => User.create({
name,
about,
avatar,
email,
password: hash,
}))
.then((user) => {
if (!user) {
throw new InvalidInputError("Invalid data");
}
res.send(user); // response includes password field
})
.catch((err) => next(err));
};
However, if I add User.findById query after User.create, I get a response without password field.
// controllers/user.js
// ...
.then((user) => {
if (!user) {
throw new InvalidInputError("Invalid data");
}
return User.findById(user._id);
})
.then((user) => {
if (!user) {
throw new NotFoundError("User not found");
}
res.send(user); // works fine!
})
Am I right that {select: false} works only on find queries in mongoose? Are there any other workarounds for not returning password field after User.create method?
the result of save is a object model, you should convert it to a object and delete password key, like this:
user = user.toObject()
delete user.password
res.send(user); // response includes password field

FindOneAndUpdate MongoDB with Mongoose updating user's profile not working with email?

I'm building a REST API in Express JS and Mongo DB. I'm utilising the Mongoose package to interface with the database and am trying to implement a robust endpoint for allowing a user to update details about their account, for instance, their name.
My endpoint is setup, and I can send a request, however, it only seems to work if I don't pass through the email and password of the user I want to update (bearing in mind I need to check that the email they're changing it to doesn't already exist)
router.patch('/update', verify, async (req, res) => {
// validate incoming data
const { error } = registerValidation(req.body)
if (error) return res.status(400).send(error.details[0].message)
// check if user already exists
const emailExists = await User.findOne({ email: req.body.email })
if (emailExists) return res.status(400).send('Email already exists')
// hash password
const salt = await bcrypt.genSalt(10)
const hashPassword = await bcrypt.hash(req.body.password, salt)
// update user
const user = new User({
name: req.body.name,
email: req.body.email,
password: hashPassword
})
User.findOneAndUpdate({ _id: req.user.id }, user, { runValidators: true, useFindAndModify: false, new: true }, (error, result) => {
if (error) {
console.log('error')
return res.status(400).send(error)
}
res.status(200).send(result)
});
})
For a starters, I'm getting a returned 1 instead of an object, but earlier I was getting a Mongo DB error with the code of 66 for some reason...
What's going on here?
UPDATE
When sending my body to update a user, I get a response of: {"operationTime":"6845592053464694785","ok":0,"code":66,"codeName":"ImmutableField","$clusterTime":{"clusterTime":"6845592053464694785","signature":{"hash":"+uHFhNsV5B60qR/Yhd2qg8Cd6jA=","keyId":"6843771360993345539"}},"name":"MongoError"}, even though I've only edited the name out of the three fields
First, you should get a field to make a query. You should know the _id or you should know the previous email or any unique field value.
You created a new user by the following code. But this created user also will have a unique id.
const user = new User({
name: req.body.name,
email: req.body.email,
password: hashPassword
})
But if you need to update, you should not create a new user.
A user never knows his id. So you should query your data by using any other field like email. You may be in need of this code
await User.findByIdAndUpdate({email:req.body.existingemail},{
name: req.body.name,
email: req.body.email,
password: hashPassword
})
So the code which will help you may be like this,
router.patch('/update', verify, async (req, res) => {
// validate incoming data
const { error } = registerValidation(req.body)
if (error) return res.status(400).send(error.details[0].message)
// check if user already exists
const emailExists = await User.findOne({ email: req.body.email })
if (emailExists) return res.status(400).send('Email already exists')
// hash password
const salt = await bcrypt.genSalt(10)
const hashPassword = await bcrypt.hash(req.body.password, salt)
// update user
const user ={
name: req.body.name,
email: req.body.email,
password: hashPassword
}
//make sure you pass existingemail in postman
User.findOneAndUpdate({ email:req.body.existingemail }, user, { runValidators: true, useFindAndModify: false, new: true }, (error, result) => {
if (error) {
console.log('error')
return res.status(400).send(error)
}
res.status(200).send(result)
});
})

Finding _id of user just created in a route

I am making a rapid prototype of a MERN application, I have a backend question: I have a User model and a Category model, when a user sign up, I need to fill the category model with some basic informations exported from an object the user can edit later. I would like to assign every category a ref to the just created account id. The problem is I don't understand how I can retrieve the just created user id.
Here is my route (yes is not refactored, sorry):
// #route POST api/users/register
// #desc Register user
// #access Public
router.post('/register', (req, res)=>{
//Validate req.body
const {errors, isValid} = validateRegisterInput(req.body);
//Check validation
if(!isValid){
return res.status(400).json(errors); 
}
User.findOne({ email: req.body.email })
.then(user => {
if(user){
errors.email = 'Email already exists';
return res.status(400).json(errors)
} else {
const avatar = gravatar.url(req.body.email, {
s: '200', //Size
r: 'pg', //Rating
d: 'mm' //Default
});
const newUser = new User({
name: req.body.name,
email: req.body.email,
avatar,
password: req.body.password
});
//Hash the password and save
bcrypt.genSalt(10, (err, salt)=>{
bcrypt.hash(newUser.password, salt, (err, hash)=>{
if(err) throw err;
newUser.password = hash;
newUser.save()
.then(user => res.json(user))
.catch(err => console.log(err))
})
});
//Fill user categories with default categories
defaultIcons.map((icon) => {
const newCategory = new Category ({
name: icon.name,
type: icon.type,
icon: icon.icon
})
newCategory.save();
});
}
})
});
And this is the Category Schema:
//Create Schema
const CategorySchema = new Schema({
//Every account is associated with actual User schema by id
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
name: {
type: String,
required: true
},
type: {
type: String,
required: true
},
icon: {
type: String,
required: true
}
})
What would be the best solution for this? Is it better to have a separated schema for the categories like I am doing or I can implement an array of objects field in the User schema?
The part where you do
if(err) throw err;
newUser.password = hash;
newUser.save()
.then(user => res.json(user))
.catch(err => console.log(err))
you have the user in your resolved promise. you can just
const newCreatedUserID = user._id
to get the just created user ID.

Resources