should I validate uniques in Mongoose before using save? - javascript

I am new to Node and Javascript in general and I was wondering if I should validate uniqueness by using FindOne before using .save.
My User schema does have Unique:true set for email and username and my current code works like a charm since mongoose returns an error message for uniques.
I wanted to know if it was better to validate for uniqueness before attempting to save for effiency or something?
Current code as follow :
export const createUser = (data) => {
return new Promise( async (resolve, reject) => {
const userData = JSON.parse(data);
const newUser = new User(userData);
await newUser.save((err) => {
if(err){
const msg = err.errmsg.toLowerCase();
const errormsg = msg.includes('email') ? 'Email already in use' : msg.includes('username') ? 'Username already in use' : 'Unexpected error.'
reject(JSON.stringify({error: errormsg}));
}
resolve(JSON.stringify({status: 200, created: true}));
});
});
};
Implemented here :
public register(req, res){
validateRegisterForm(req.body).then(data => {
createUser(data).then(resp => {
res.send(resp);
}).catch(err => {
res.send(err);
})
}).catch(err => {
res.send(err);
});
}

Related

How to read the specific data from mongodb using mongoose?

I have data that already saved at mongoodb atlas, but i dont know how to get and display that data to my bot discord reply.
This is how i submit the data
const subregis = "!reg ign:";
client.on("message", msg => {
if (msg.content.includes(subregis)){
const user = new User({
_id: mongoose.Types.ObjectId(),
userID: msg.author.id,
nickname: msg.content.substring(msg.content.indexOf(":") + 1)
});
user.save().then(result => console.log(result)).catch(err => console.log(err));
msg.reply("Data has been submitted successfully")
}
});
This is my schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const profileSchema = new Schema({
_id: mongoose.Schema.Types.ObjectId,
userID: String,
nickname: String,
});
module.exports = mongoose.model("User", profileSchema);
And i want to show the data like this, i try this code but didnt work.
client.on("message", msg => {
if (msg.content === "!nickname"){
msg.reply("Your Nickname:", User.findById(nickname))
}
});
In MongoDB, you have a few ways to query data from the database. Some of them are: User.find() (to find multiple documents), User.findById() (to get a document by its id), and User.findOne (to find only the first document which matches the parameters). An example of each of them would be:
User.find({ query }, function (err, data) {
if (err) throw err
console.log(data) // This will return all of the documents which match the query
})
User.findById({ id }, function (err, data) {
if (err) throw err
console.log(data) // This will return the document with the matching id given
})
User.findOne({ query }, function (err, data) {
if (err) throw err
console.log(data) // This will return the first document which matches the query
})
To find the data by the nickname, you would first have to get it by splitting the message content. Then you would have to query the data by using one of the methods mentioned above and then you can respond back. You can do something like this:
client.on('message', async (message) => {
const args = message.slice(1).split(' ')
const command = args.shift().toLowerCase()
const nickname = args.join(' ')
const data = await User.findOne({ userId: message.author.id })
if (!data) return
message.channel.send(`The nickname is ${nickname}`)
})
you can define the Schema by using
const data = Schema.findOne({ UserID: message.author.id })
const nick = data.nickname;
if (!data) return message.reply({content: 'You have no data'})
message.reply({content: `Your nickname is ${nick}`})
Or you can bring the schema and use .then()
Schema.findOne({ userID: message.author.id }, async (err, data) => {
// your code here
});
Don't forget to add your schema folder path
const Schema = require('...') // your schema file
this way it search for the data in database using the userID because findbyId() is the main mongodb collection id
findById() method finds by _id field. So you can either do this:
client.on("message", msg => {
if (msg.content === "!nickname"){
// reply by User find by _id mongoose
User.findById(id, (err, user) => {
if (err) return console.error(err);
msg.reply(`Your nickname is ${user.nickname}`);
});
}
});
Or do this if you want to query with nickname:
client.on("message", msg => {
if (msg.content === "!nickname"){
// reply by User find by nickname mongoose
User.findOne({nickname: "nickname"}, (err, user) => {
if (err) return console.log(err);
msg.reply("Your Nickname:", user.nickname);
}
);
}
});
You need to pass the actual Mongo ID into User.findById, if you want to find by userID or nickname write something like
User.find({ nickname })

How to check for a valid Object Id in mongoose?

I have a route that returns a particular story from an object Id. When i try testing it, it gives me some errors. The code inside if block is not executing somehow.
router.get("/:id",async (req,res) => {
try{
if (!isValidObjectId(req.params.userId)) {
res.status(401).json({
message: "Invalid object id",
success: false
})
throw new Error("Invalid object id")
}
let story = await Story.findById(req.params.id)
.populate('user')
.lean()
if (!story) {
return res.status(404).json({
message: "Story not found",
success: false
})
}
const text = convert(story.body, {
wordwrap: null
});
res.render('stories/show',{
story,
title: `${story.title} Storybooks`,
desc: `${text}`
})
}
catch(err) {
console.error(err)
}
})
I don't want to execute the query if the id is not valid say /stories/blabla
How can i do that?
Your response is appreciated.
For those of you struggling with the problem here is a time saver:
First we us the method isValid on mongoose.Types.ObjectId then as a 2nd check we create an actual object id an compare it as a string.
Here's how you would import and use it:
const mongoose = require('mongoose');
const {Types: {ObjectId}} = mongoose;
const validateObjectId = (id) => ObjectId.isValid(id) && (new ObjectId(id)).toString() === id; //true or false
As to answering my own question:
const mongoose = require('mongoose');
const {Types: {ObjectId}} = mongoose;
const validateObjectId = (id) => ObjectId.isValid(id) && (new
ObjectId(id)).toString() === id; //true or false
// #desc Show a single story
// #route GET /stories/:id
router.get("/:id",async (req,res) => {
try{
if (!validateObjectId(req.params.id)) {
throw Error("Invalid object Id")
}
let story = await Story.findById(req.params.id)
.populate('user')
.lean()
if (!story) {
return res.status(404).json({
message: "Story not found",
success: false
})
}
const text = convert(story.body, {
wordwrap: null
});
res.render('stories/show',{
story,
title: `${story.title} Storybooks`,
desc: `${text}`
})
}
catch(err) {
console.error(err)
}
})
EDIT:
I used req.params.userId instead of req.params.id so the above method is totally fine.
But just learnt a new way of doing it.

Make a login and registration system using Azure functions and Azure SQL server

I want to make a Dating application using node.js and javascript with Azure functions and an Azure sql server. I can create a user so it appears in my database, but how do I make a login system that "checks" if the users email and password is in the database and is correct.
This is what I have so far:
**Login.js:**
var form = document.getElementById("form")
form.addEventListener('submit', function(e) {
e.preventDefault()
var email = document.getElementById("email").value
var password = document.getElementById("password").value
fetch("http://localhost:7071/api/login", {
method: 'POST',
body: JSON.stringify({
email: email,
password: password,
}),
headers: {
"Content-Type": "application/json; charset-UTF-8"
}
})
.then((response) => {
return response.text()
})
.then((data) => {
console.log(data)
}).catch((err) =>{ // catcher fejl, hvis noget går galt
console.log("wuups: " + err)
})
})
**DB.js connect:**
function login (payload) {
return new Promise((resolve, reject) => {
const sql = 'SELECT * FROM [user] where email = #email AND password = #password'
const request = new Request(sql,(err,rowcount) =>{
if (err){
reject(err)
console.log(err)
} else if( rowcount == 0){
reject({messsage:"user does not exit"})
}
});
request.addParameter('email', TYPES.VarChar, payload.email)
request.addParameter('password', TYPES.VarChar, payload.password)
request.on('row',(colums) => {
resolve(colums)
})
connection.execSql(request)
return "you are now logged in"
});
}
module.exports.login = login;
You're on the right track. Consider an updated version of db.sql:
function login(payload, connection) {
return new Promise((resolve, reject) => {
const sql = 'SELECT * FROM [user] where email = #email AND password = #password'
const request = new Request(sql, (err, rowCount) => {
if (err) {
reject(err)
console.error(err)
}
else {
if (rowCount == 1) {
resolve(payload.email)
}
else {
reject('Invalid credentials')
}
}
});
request.addParameter('email', TYPES.VarChar, payload.email)
request.addParameter('password', TYPES.VarChar, payload.password)
connection.execSql(request)
});
}
Since we can infer a successful login from the amount of returned rows, we don't need access to the actual rows in the row callback.
However: as pointed out by Robert in the comments, storing passwords in plain text is a security concern (since access to the database immediately unveils user passwords).
Better approach
The better approach is to store hashed passwords instead. Imagine this simple user table schema in MSSQL:
CREATE TABLE [User] (
[Email] [varchar](max) NOT NULL UNIQUE,
[PasswordHash] [varchar(max)] NOT NULL
)
The login procedure will remain almost the same. Instead of comparing passwords we now compare hashed passwords. Without going into too much detail, you would usually use a library for this purpose (to handle salts, mitigate timing attacks, etc.). I chose bcryptjs for the example below:
var bcrypt = require('bcryptjs');
function login(email, password, connection) {
return new Promise((resolve, error) => {
const sql = 'SELECT * FROM [user] where email = #email' // Note that the password comparison no longer lives here
const request = new Request(sql, (err, rowCount) => {
if (err) {
reject(err)
}
})
request.addParameter('email', TYPES.VarChar, email)
let userRow = null
// This time we need the 'row' callback to retrieve the password hash
request.on('row', row => {
userRow = {
email = row[0].value,
passwordHash = row[1].value
}
})
// .. and the 'done' callback to know, when the query has finished
request.on('done', rowCount => {
if (rowCount == 0) {
reject('User not found')
}
else {
bcrypt.compare(password, userRow.passwordHash) // Password comparison
.then(passwordsMatch => {
if (passwordsMatch) {
resolve(email)
}
else {
reject('Invalid credentials')
}
})
}
})
connection.execSql(request)
})
}
And here's an example of how to create new users with this approach using the same library:
var bcrypt = require('bcryptjs');
const PASSWORD_SALT_ROUNDS = 10 // Learn more at ex. https://stackoverflow.com/questions/46693430/what-are-salt-rounds-and-how-are-salts-stored-in-bcrypt
function createNewUser(email, password, connection) {
return bcrypt.hash(password, PASSWORD_SALT_ROUNDS).then(passwordHash => {
const sql = 'INSERT INTO [user] (Email, PasswordHash) VALUES (#email, #passwordHash)'
const request = new Request(sql, err => {
if (err) {
error(err)
}
else {
resolve()
}
})
request.addParameter('Email', TYPES.VarChar, email)
request.addParameter('PasswordHash', TYPES.VarChar, passwordHash)
connection.execSql(request)
})
}
Consider this a pragmatic proposal to get started. Please note, that the code is illustrative, since I haven't actually executed it, and it is made under certain assumptions.

How to debug a mongoose action within node.js

I am using mongoose to connect mongoDB and my node.js app. However, when I create or update a model instance, it won't change the Database, how can I go inside to debug what happens in the create or update action? I do check the MongoDB interface, delete and find and list action works just fine:
Here are those two docs that I have:
// index.js
const mongoose = require('mongoose')
const User = require('../model/user')
mongoose.Promise = global.Promise;
// connect to DB
const db = mongoose.connect('mongodb://localhost:27017/myImportantDates', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
// create a user
const addUser = (user) =>{
let newUser = new User(user)
if (newUser.save()){
console.log(newUser) // it will console.log the newly created user, but it is not in the database
mongoose.disconnect()
}else{
console.log(newUser.errors)
}
}
// list all users
const listAllUsers = () =>{
User.find().then((users)=>{
console.log("Totally there are " + users.length + " users.");
console.log(users);
mongoose.disconnect();
}).then(()=>process.exit())
}
// find one user
const findUserByEmail = (email) => {
User.find({email},(err,docs)=>{
if(err){
console.log(err)
}else{
console.log(`Already found ${docs.length} matches.` )
console.log(docs)
}
mongoose.disconnect()
})
}
// update a user and make sure pass {new:true} option so that the doc in callback return the doc after updated
const updateUser = (email,user) => {
User.findOneAndUpdate( { email }, user, { new: true }, (err,doc) =>{
if(err){
console.log(err);
return
}else{
console.log(doc)
}
mongoose.disconnect()
})
}
// remove a user
const deleteUser = email => {
User.deleteOne( { email },(err,res) =>{
if(err){
console.log(err);
return
}
console.log("Deleted Successfully.");
mongoose.disconnect()
})
}
module.exports = {
addUser,
listAllUsers,
findUserByEmail,
updateUser,
deleteUser
}
//user_methods.js
const { program } = require('commander');
const {addUser,listAllUsers,findUserByEmail,updateUser,deleteUser} = require('./model_methods/user_methods')
const inquirer = require('inquirer')
const questions = [
{
type: 'input',
name: 'name',
message: 'user name'
},
{
type: 'input',
name: 'email',
message: 'user email'
},
{
type: 'input',
name: 'password',
message: 'user password'
},
];
program
.version('0.0.1')
.description("testing");
program
.command('list')
.alias('l')
.description('List all users')
.action(()=>listAllUsers())
program
.command('add')
.alias('a')
.description('Add a user')
.action(()=>{
inquirer.prompt(questions)
.then( answers => {
addUser(answers)
}).then(() => {
process.exit()
})
.catch(err =>{
console.log(error)
})
})
program
.command('find <email>')
.alias('f')
.description('find a user through email')
.action((email)=>{
findUserByEmail(email)
})
program
.command('update <email>')
.alias('u')
.description('update a user through email')
.action((email)=>{
inquirer.prompt(questions)
.then( ( email,answers ) => {
updateUser(email, answers)
}).then(() => {
process.exit()
})
.catch(err =>{
console.log(error)
})
})
program
.command('delete <email>')
.alias('d')
.description('delete a user through email')
.action((email)=>{
deleteUser(email)
})
program.parse(process.argv)
I will run node index.js <command> to reach those methods.
Currently, the process is getting exited before the save happens. According to your current code, it looks like you don't need to call the process.exit() explicitly. The application will exist on its own when the addUser operation is completed.
Also, you need to update the addUser method. You should only close the connection after successfully saving the record
// create a user
// create a user
const addUser = (user) =>{
let newUser = new User(user)
newUser.save((err, result) => {
console.log("inside save method")
if (err) console.log(err);
else {
console.log(result);
mongoose.disconnect();
}
})
}

Make query for every object in json using for or forEach

My problem is, I want to make INSERT query for every object from JSON using some loop, but I almost always got an error "Cannot set headers after they are sent to the client".Can someone help?Tnx
const connection = require('./config');
module.exports.excel = function (req, res) {
var _query = 'INSERT INTO excel (id, first_name, last_name) values ?';
var jsonData = req.body;
var values = [];
function database() {
return new Promise((resolve, reject) => {
jsonData.forEach((value) => {
values.push([value.id, value.first_name, value.last_name]);
connection.query(_query, [values], (error, results) => {
if (error) {
reject(
res.json({
status: false,
message: error.message
}))
} else {
resolve(
res.json({
status: true,
data: results,
message: 'Excel file successfully created in database'
}))
}
});
});
})
}
async function write() {
await database();
}
write();
}
After I got JSON from my Angular 6 front I put req.body into jsonData and try with forEach to put every object("value" in this case) into query and write that into Excel file.
You will have to wrap each query in a Promise and wait for all to complete before sending the response using Promise.all
Not that database() is going to throw when one of the queries fail and you won't have any access to the resolved promises.
const connection = require('./config');
module.exports.excel = function(req, res) {
const _query = 'INSERT INTO excel (id, first_name, last_name) values ?';
const jsonData = req.body;
function database() {
return Promise.all(
jsonData.map(
value =>
new Promise((resolve, reject) => {
const values = [value.id, value.first_name, value.last_name]
connection.query(_query, [values], (error, results) => {
if (error) {
reject(error.message);
return;
}
resolve(results);
});
})
)
);
}
async function write() {
try {
const results = await database();
res.json({
status: true,
data: results,
message: 'Excel file successfully created in database'
});
} catch (e) {
res.json({
status: false,
message: e.message
});
}
}
write();
};

Categories

Resources