Handle duplicates with express.js - javascript

So I am trying to create a login for the first time with express and react using Postgres. My user can be added to the database so I jumped into handling duplicates. I am using the findUserByEmail function to find my email and then, in my routes, calling that function before posting the email. I hope you guys can help me, I've been 1 week with this.
This is my queries.js where I :
const findUserByEmail = email => {
return pool.query("SELECT * FROM users WHERE email = $1", [email]);
};
const createUser = (request, response) => {
const date_created = new Date();
const { username, email, password } = request.body;
bcrypt.genSalt(saltRounds, function(err, salt) {
bcrypt.hash(password, salt, function(err, hash) {
pool.query(
`INSERT INTO users (username, email, password, date_created) VALUES ($1, $2, $3, $4 )`,
[username, email, hash, date_created],
(error, results) => {
// console.log("---------->", email);
if (error) {
throw error;
}
response.status(201).send(`User added with ID: ${results.insertId}`);
}
);
});
});
};
and this is my index.js:
// ...Other connection code
//Routes
app.get("/users", queries.getUsers);
app.get("/user/:id", queries.getUserById);
app.post("/signup/user", (req, res, next) => {
console.log(req.body.email, "----------1");
queries
.findUserByEmail(req.body.email)
.then(user => {
console.log(user.rows.length);
if (user.rows.length < 0) {
res.status(400).send("this email is already in use");
} else {
console.log("Hello");
queries.createUser;
}
})
.catch(err => {
console.log(err);
res.status(500).send("Something went wrong");
});
});
app.put("/user/:id", queries.updateUser);
app.delete("/user/:id", queries.deleteUser);
app.listen(port, () => {
console.log(`App running on port ${port}.`);
});
Is not giving me an error, but when I submit a new user, keeps posting forever and does not change.
My post request worked till now without using the findUserByEmail.
Thanks for your help.

You have no guarantee that findUsersByEmail will find anything, even if there is a previous call, creating a user. The reason is that the query creating the user may not have returned yet.
You need database constraints to deal with this.
Put a unique constraint on the email field and handle duplicate exceptions correctly.

Related

Cannot set headers after they are sent to the client using nodejs mysql

What I am attempting to do is write a statement to check if email exists in my mysql database when a user registers. In postman it sends me the correct error message of "user already taken" however the server crashes after and displays "cannot set headers after they are sent to the client." I have read similar posts but did not help.
//The following code is in my user.service.js file:
const pool = require("../../config/database");
module.exports = {
//Create new user
createUser: (data, callBack) =>{
pool.query(
`insert into registration(name, email, password, confirm_password)
values(?,?,?,?)`,
[
data.name,
data.email,
data.password,
data.confirm_password
],
(error, results, fields) =>{
if(error){
return callBack(error);
}
return callBack(null, results);
}
);
}
}
//The following code is in my user.controller.js file:
const {
createUser,
} = require("./user.service");
const pool = require("../../config/database");
module.exports = {
createUser: (req, res) =>{
const body = req.body;
const salt = genSaltSync(10);
pool.query('SELECT email FROM registration WHERE email = ?', [body.email], (error, results) =>{
if(error){
console.log(error);
}
if(results.length > 0){
return res.status(400).json({
message: 'User already taken'
})
}
})
createUser(body, (err, results) => {
if(err){
console.log(err);
return res.status(500).json({
success:0,
message:"Error in database connection"
});
}
return res.status(200).json({
success: 1,
message: `User ${results.insertId} signed up successfully`,
data: results
});
});
}
}
//The following code is from user.router.js file:
const {
createUser,
} = require("./user.controller");
const router = require("express").Router();
router.post("/signup", createUser);
module.exports = router;
In your createUser function that is executed on the post request you are doing two things. First you check whether a user with the provided email exists and, second, you create a user. However, those functions are not executed consecutively, instead they are running simultaneously and thus create a race condition.
So going off on your example, if the email check query SELECT email FROM registration WHERE email = ? is faster and the user already exists, it will respond with:
return res.status(400).json({
message: 'User already taken'
})
but the createUser function (below) is still running and once it is finished, it will try to also send a response. Therefore, you are presented with an application crash in the console even though in the postman you can see the response stating that the user already exists.
In order to fix this error you should execute the createUser function only if the results.length is 0 inside the callback provided to the email check query, like so:
createUser: (req, res) => {
const body = req.body;
const salt = genSaltSync(10);
pool.query('SELECT email FROM registration WHERE email = ?', [body.email], (error, results) =>{
if(error){
console.log(error);
}
if(results.length > 0){
return res.status(400).json({
message: 'User already taken'
})
}
createUser(body, (err, results) => {
if(err){
console.log(err);
return res.status(500).json({
success:0,
message:"Error in database connection"
});
}
return res.status(200).json({
success: 1,
message: `User ${results.insertId} signed up successfully`,
data: results
});
});
})
}
Now you execute the createUser function only if a user with the provided email doesn't exist, which effectively removes the race condition between the two functions.

Node.js - PostgreSQL (pg) : Client has already been connected. You cannot reuse a client

I am just trying to write simple register/login system.
I am trying to find if username exists. Here is the steps :
Go localhost:3000/users/register page
Fill in all fields and click register button
Checking my command line if username exists it should print it with console.log
Everything works fine until now.
When I go back to the register page, I fill in all fields again and click register button. Then it throws it in command line :
Error: Client has already been connected. You cannot reuse a client.
at Client._connect (C:\Users\Hasan\Desktop\Projeler\node-ogreniyorum\node_modules\pg\lib\client.js:91:17)
at C:\Users\Hasan\Desktop\Projeler\node-ogreniyorum\node_modules\pg\lib\client.js:310:10
at new Promise (<anonymous>)
at Client.connect (C:\Users\Hasan\Desktop\Projeler\node-ogreniyorum\node_modules\pg\lib\client.js:309:10)
at Object.module.exports.findUserById (C:\Users\Hasan\Desktop\Projeler\node-ogreniyorum\database\register_sql.js:8:22)
at C:\Users\Hasan\Desktop\Projeler\node-ogreniyorum\routes\users.js:37:29
at Layer.handle [as handle_request] (C:\Users\Hasan\Desktop\Projeler\node-ogreniyorum\node_modules\express\lib\router\layer.js:95:5)
at next (C:\Users\Hasan\Desktop\Projeler\node-ogreniyorum\node_modules\express\lib\router\route.js:137:13)
at next (C:\Users\Hasan\Desktop\Projeler\node-ogreniyorum\node_modules\express\lib\router\route.js:131:14)
at Route.dispatch (C:\Users\Hasan\Desktop\Projeler\node-ogreniyorum\node_modules\express\lib\router\route.js:112:3)
I dont understand because I already end my client after I call my method.
register_sql.js :
module.exports.findUserById =(async (username) =>{
try {
await client.connect();
console.log('Connected successfuly');
const result = await client.query("select * from users where username = ($1)", [username]);
console.log(result.rows[0]['username']);
await client.end();
console.log('Client disconnected...');
} catch (err) {
console.log(err);
}
});
I call register_sql.js in users.js. Here is users.js :
const router = express.Router();
const registerSQL = require('../database/register_sql');
router.route('/register')
.get((req, res, next) => {
res.render('register');
})
.post((req, res, next) => {
const {
username,
password,
password2
} = req.body;
let errors = [];
if (!username || !password || !password2) {
errors.push("Please fill in all fields!");
}
if (password != password2) {
errors.push("Passwords do not match!");
}
if (password.length < 6) {
errors.push("Password has to be at least 6 characters!");
}
if (errors.length > 0) {
res.render('register', {
errors
});
} else {
registerSQL.findUserById(username);
res.redirect('/');
}
});
module.exports = router;
Thank you for helping!
With node-postgres (which is the one you're using) I've only made it work using the pool do manage the connections.
const pg = require('pg')
const pool = new pg.Pool();
pool.connect(function(err, client, done) {
if(err) {
return console.error('connexion error', err);
}
client.query("select * from users where username = ($1)", [username], function(err, result) {
// call `done()` to release the client back to the pool
done();
if(err) {
return console.error('error running query', err);
}
console.log(result.rows[0]['username'])
});
});
I had the same problem, dont create the new Client outside the function.
- const client = new pg.Client(connection);
-
function test() {
+ const client = new pg.Client(connection);
+
client.connect(err => {
if (err) {
console.log(err);
return;
}
client.query('select 123', [], (err, data) => {
if (err) {
console.log(err);
} else {
console.log('DATA:', data.rows[0]);
}
client.end();
});
});
}
I managed to fix this problem without using pool. Maybe that's not the most correct solution, but it works.
First create a separate js file, where you connect the client and export it:
const pg = require('pg')
const client = new pg.Client('your connection string')
client.connect()
module.exports = client
Then you just use the exported client, which has already been connected, so it won't reconnect again on each request. Be sure to import the client from the js file where you connect it.
const client = require('../db')
const register = async (req, res) => {
const {email, password, username} = req.body
const hashedPassword = await bcrypt.hash(password, 10)
const command = `insert into users(email, username, password, created) VALUES ('${email}', '${username}', '${hashedPassword}', current_timestamp)`
await client.query(command, (err, result) => {
err ? res.json({error: err.detail}) : res.json({message: 'User created!'})
})
}
well the problem occur because you haven't closed the connection to database.
Remember you have to close the connection before you sent something to client like that:
try {
await client.connect();
const result = await client.query(query, values);
await client.end();
res.status(201).send({
result,
});
} catch (err) {
return res.send({
error: err.detail,
message: "Can't create a new user, please check your info again!",
});
}
Pool approach is better practice but if someone want to connect with Client approach then this solution will work.
Code which will work with Client approach :
client.connect();
client.query(`select * from users where username = ($1)`, (err, result)=>{
try{
console.log("Test", result)
res.send(result)
} catch{
console.log("err", err)
res.status(500).json(err);
}
client.end();
})

express.js retrieve username from mysql database doesn't redirect to signin page

I'm trying to retrieve username from mysql database, the below code can successfully retrieve the username. However, when err occurs, code doesn't redirect to /signin. The page redirect to /admin instead. Though, if I add
res.redirect('/signin')
just before the last curly bracket, it will redirect to the signin page, but it won't able to retrieve the username.
I want it to redirect to signin page, how?
const connection = require('./connection')
const signin = (req, res) => {
var email = req.body.email
var password = req.body.password
let queryStr = `select username from users where email='${email}' and password='${password}'`
return connection.query(queryStr, (err, rows, fields) => {
if (err) {
res.redirect('/signin')
} else {
req.session.email = email
res.json(rows[0])
}
})
}
module.exports = signin
I think it has to do with async because the code execute the last line then go back to the else statement. I think that's why it goes to /admin page instead. but not fixed yet.
connection.js
const mysql=require('mysql')
var connection=mysql.createConnection({
host:'localhost',
user:'root',
password:'root',
database:'essencejoin',
})
connection.connect()
connection.query('SELECT 1 + 1 AS solution', function (err, rows, fields) {
if (err) throw err
console.log('The solution is: ', rows[0].solution)
})
module.exports=connection
Actually, You're Query is correct, But query doesn't return any rows. So if user not found on username table. It needs to signin page(This is your question correct?), For that you have to update the code like following,
const connection = require('./connection')
const signin = (req, res) => {
var email = req.body.email;
var password = req.body.password;
let queryStr = `select username from users where email='${email}' and password='${password}'`;
return connection.query(queryStr, (err, rows, fields) => {
if (err) {
res.redirect('/signin');
} else if(!rows.length) {
res.redirect('/signin');
}else {
req.session.email = email;
res.json(rows[0]);
}
})
}
module.exports = signin
It looks your code is being executed after you have returned it. You can use promisified version of the same code.
use mysql-promise. this is promisified wrapper around the same library you are using. then Your code will look something like this:
// install mysql-promise
npm i mysql-promise
connection.js
const mysql = require("mysql-promise");
async function getDBConn() {
const connection = mysql.createConnection({
host: "localhost",
user: "root",
password: "root",
database: "essencejoin"
});
await connection.connect();
return connection;
}
module.exports = getDBConn;
then use it like this:
//signin.js
const signin = (req, res) => {
const email = req.body.email;
const password = req.body.password;
const connection = getDBConn();
let queryStr = `select username from users where email='${email}' and password='${password}'`;
try {
await connection.query(queryStr);
req.session.email = email;
res.json(rows[0]);
} catch (error) {
console.log(error);
res.redirect("/signin");
}
};
module.exports = signin;
disclaimer: I have not tested this, even if this does not work this should give you a fair idea how to make it work
Without knowing too much about your project, I think you want to look into adding a status code with the express redirect.
index(req, res, next) {
topicQueries.getAllTopics((err, topics) => {
if (err) {
res.redirect(500, "static/index");
} else {
res.render("topics/index", { topics });
}
});
}
Something like that. Also, look out for other simple mistakes in your project, that may cause this, that deal with Express.
https://expressjs.com/en/api.html#res.redirect

Update user information

Tried this for updating user information , only phone number but it's not getting update.
router.post('/edit', checkAuth, function (req, res, next) {
console.log(req.userData.userId)
User.update({_id: req.userData.userId}, {$set:req.userData.phoneNo}, function (err){
if (err) {
console.log(err);
}
res.status(200).send(req.userData);
});
});
My user controller const mongoose = require ('mongoose');
const User = mongoose.model('User');
module.exports.register = (req, res, next) =>{
var user = new User();
user.fullName = req.body.fullName;
user.email = req.body.email;
user.password = req.body.password;
user.phoneNumber = req.body.phoneNumber;
user.save((err, doc) =>{
if(!err)
res.send(doc);
else{
if (err.code == 11000)
res.status(422).send(["Entered duplicate email address. Please check"]);
else
return next(err);
}
});
}
And then I am authenticating by passing jwt on this field
phoneNo: user[0].phoneNumber
The auth-token verifies and decode the fields
const token = req.headers.authorization.split(" ")[1];
const decoded = jwt.verify(token, process.env.JWT_KEY)
req.userData = decoded;
Update is not working and getting error message Invalid atomic update value for $set. Expected an object, received string .
first of all, you should use PATCH-method - because you are updating only one item in existed object, in body you should send id of user and new value of certain value. If you use mongoose you can try it
User.findOneAndUpdate({ _id: id }, updatedItem, { new: true }, (err, doc) => {
if (err) return res.send(err.message)
if (doc) return res.send(doc);
})
const id = req.body._id;, if you dont use mongoose you should try findAndModify method
Your code
User.update({_id: req.userData.userId}, {$set:req.userData.phoneNo}
Correct code:
User.update({_id: req.userData.userId}, {$set:{phoneNumber:req.userData.phoneNo}}
Try this method:
User.findByIdAndUpdate(req.userData.userId, { $set:{phoneNumber:req.userData.phoneNo}}, { new: true }, function (err, user) {
if (err) console.log(err);
res.send(user);
});

How to add a session in Node js

we're working on a project for our Programming-Course. We're doing a very small social media platform, where you can register, login, create a profile, view events and log out.
So I set the session variable for logging in, but I also want that people who register are instantly logged in and get redirected to the profile-site. So I have to set another session into the registration app.post I guess, but I have absolutly no Idea how to do this (because I'm a bloody beginner)... Can anybody help? This is the code so far:
//------------Sessionvariables---------------//
app.get('/', requiresLogin, function(req, res) {
res.render('home', {
'username': req.session.user.name
});
});
app.post('/sendLogin', function(req, res) {
//in Datenbank gucken
const user = req.body["username"];
const password = req.body["password"];
db.get(`SELECT * FROM users WHERE username='${user}'`, function(err, row) {
if (row != undefined) {
if(password == row.password) {
req.session['user'] = user;
res.redirect('/home');
}else{
res.redirect('/loginerror');
}
}else{
res.redirect('/loginerror');
}
if(err){
console.error(err.message);
}
});
});
app.get('/logout', function(req, res){
req.session.destroy(function (err) {
if (err) return next(err)
req.session = null;
res.redirect('/start-login');
});
});
// Registration
app.post('/registration', function(req, res) {
const { email, password, username, domicile } = req.body;
// validation
db.run(`INSERT INTO users(email,password,username,domicile) VALUES(?, ?, ?, ?)`, [email, password, username, domicile], function(err) {
if (err) {
return console.log(err.message);
}
return res.redirect('/edit_profile');
});
});
I know that I have to write the session into the app.post /registration - Part, but I don't know how to write it. I'm using Node js, Express and sqlite3...
Thank you!!
Why not just do:
// Registration
app.post('/registration', function(req, res) {
const { email, password, username, domicile } = req.body;
// validation
db.run(`INSERT INTO users(email,password,username,domicile) VALUES(?, ?, ?, ?)`, [email, password, username, domicile], function(err) {
if (err) {
return console.log(err.message);
} else {
// Create the session / save any data you want here
req.session.user.name = username;
/* I would actually call somthing like: createSession(req)
dedicated function that nicely creates the session */
// Redirect to home just like you do after successful login
res.redirect('/home'); // Redirect to home since user registered successfully
}
});
});
By the way always treat error before running other code logic i see you are treating errors at the end of code (in the app.post('/sendLogin',...) :
if(err){
console.error(err.message);
}
Write like this:
if(err){
console.error(err.message);
// and res.end() or similar if its in an express route
} else {
// Do safe stuff here
}
the best method i can suggest you is that after registration success set the user details in session and just redirect to your home page

Categories