I wanted to made a query to the mysql database if the username already exists he should throw me a message and if not, show me the new id. I wanted to check if the query result.lenght is higher than 0.
however it throws the error "Cannot read property 'length' of undefined"
Service file
const connect = () =>
mysql.createConnection({
host: process.env.MYSQL_HOST,
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE
});
const insert = (conn, user) => {
return conn.query(`insert into user(username, firstName, lastName, password)
values(?,?,?,?)`,
[
user.username,
user.firstName,
user.lastName,
user.password
]
)
}
const getByUsername = username => {
return connect()
.then(conn => {
conn.query(
`select id, username, firstName, lastName
from user
where username = ?`,
[username]
);
})
}
const create = user => {
return getByUsername(user.username)
.then(result => {
if (result.length > 0) {
throw new Error(`User "${user.username}" already exists`);
}
})
.then(() => connect())
.then(conn => insert(conn, user))
.then(result =>
({
id: result.insertId
}));
};
Controller file
const create = (req, res) => {
userService.create(req.body)
.then(result => {
res.status(200);
res.json(result);
})
.catch(err => {
res.status(500);
res.send('Error ' + err.message);
})
}
I tried to define lenght but I think this isnt the issue. I make the query with postman and i use mysql as database. I also tried to replace the result.length with username.length but it thorws "Error username is not defined"...
The result might be undefined there. Because we don't return proper query results. That why we getting error.
Solution:
const getByUsername = username => {
return new Promise((resolve, reject) => {
connect()
.then(conn => {
conn.query(
`select id, username, firstName, lastName
from user
where username = ?`,
[username],
function(err, results) {
if (err) {
return reject(err);
}
return resolve(results)
}
);
})
});
}
Explanation:
As per mysql2 documentation, the conn.query will not return the results. The conn.query method has a third argument which is a callback method. Here we can get the SQL query error and query results.
By the use of javascript Promise API, we can decide to reject or resolve based upon the SQL query result.
Related
So here is the controller's code which I am using to handle password generation process. There is a table called as passwordrecovery which has the following columns:-
id
uuid
email
isActive
Code
File: db.js
const mysql = require("mysql2");
const connectionPool = mysql.createPool({
user: "root",
database: "expensetracker",
host: "localhost",
password: process.env.MYSQL_PASSWORD,
});
module.exports = connectionPool;
File- users.js
const path = require("path");
const db = require("../database/db");
exports.passwordGenerator = async (req, res, next) => {
var id = req.query.id;
console.log(id);
await db.execute(
"SELECT uuid, isActive FROM passwordrecovery WHERE id = ?",
[id],
(err, results) => {
if (err) {
console.log(err);
res.status(500).send("SERVER ERROR");
} else {
//console.log(results);
const response = results[0];
const isActive = parseInt(response.isActive);
if (isActive == 1) {
db.execute(
"UPDATE passwordrecovery SET uuid = ?, isActive = 0 WHERE id = ?",
[null, id],
(err, results) => {
if (err) {
console.log(err);
return res.status(500).send("SERVER ERROR");
} else {
console.log(results);
return res
.status(200)
.sendFile(
path.join(
__dirname,
"..",
"views",
"password-reset-form.html"
)
);
}
}
);
} else {
res.status(408).send("SESSION EXPIRED");
}
}
}
);
};
Error Message
undefined
TypeError: Bind parameters must not contain undefined. To pass SQL NULL specify JS null
at D:\Projects\Expense Tracker\Backend\node_modules\mysql2\lib\connection.js:659:17
at Array.forEach (<anonymous>)
at PoolConnection.execute (D:\Projects\Expense Tracker\Backend\node_modules\mysql2\lib\connection.js:651:22)
at D:\Projects\Expense Tracker\Backend\node_modules\mysql2\lib\pool.js:172:14
at D:\Projects\Expense Tracker\Backend\node_modules\mysql2\lib\pool.js:45:37
at processTicksAndRejections (node:internal/process/task_queues:78:11)
I was expecting that this express js server would return that html(password-reset-form.html) file which it does, here is response from server but it also console logs this error message. I don't know what to, tried my best to resolve myself, read some blogs, googled some stuff but still could not solve this error!
let mysql = require('mysql');
let connection = mysql.createConnection({
host: 'localhost',
user: 'priyanka',
password: '1234',
database: 'todoapp'
});
connection.connect(function(err) {
if (err) {
return console.error('error: ' + err.message);
}
console.log('Connected to the MySQL server.');
});
// promise function
query = `select device from user_table limit 34`;
sql = function(device){
return new Promise(function(resolve,reject)
{
connection.query(query, (error, results) => {
if (error) {
reject( console.error(error.message));
}
resolve(console.log(results));
});
})
}
sql('device').then(function(rows) {
}).catch((err) => setImmediate(() => { throw err; }));
connection.end();
sql('device') -> inside sql call , can i only place comma separated field values to get rows from user_table or is there any other way to pass multiple columns ?
I created 3 functions: findOne, create and update. Respectively the methods are GET, POST, PUT.
I changed my API path, it used to be /api/users/:id, now it's /api/users/:sub.
routes.js:
module.exports = app => {
const users = require("../controllers/user.controller.js");
const router = require("express").Router();
// Create a new User
router.post("/", users.create);
// Retrieve a single User with sub
router.get("/:sub", users.findOne);
// Update a User with sub
router.put("/:sub", users.update);
// Delete a User with sub
router.delete("/:sub", users.delete);
app.use('/api/users', router);
};
controller.js:
// Save User in the database
User.create(user)
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while creating the Users."
});
});
};
// Find a single User with an id and sub
exports.findOne = (req, res) => {
const sub = req.params.sub;
User.findOne({sub})
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message: "Error retrieving User with id=" +sub
});
});
};
// Update a User by the sub in the request
exports.update = (req, res) => {
const sub = req.params.sub;
User.update(req.body, {
where: { sub }
})
.then(num => {
if (sub) {
res.send({
message: "User was updated successfully."
});
} else {
res.send({
message: `Cannot update User with sub=. Maybe User was not found or req.body is empty!`
});
}
})
.catch(err => {
res.status(500).send({
message: "Error updating User with sub="
});
});
};
What I wanted to do was: check if the user_id provided by the authentication provider was present in my database.
If yes, update user data with that user_id.
If not, create a new user record
This is the front-end part involved:
//INFO SAVE AND UPDATE CONDITION
const userExist = InfoDataService.get(data.sub)
.then((response) => {
console.log('find', response.data);
return true;
});
if ( userExist ) {
InfoDataService.create(data)
.then((response) => {
console.log('create', response.data);
setInfo({
id: response.data.id,
sub: response.data.sub,
email: response.data.email,
firstname: response.data.firstname,
lastname: response.data.lastname,
});
})
} else {
InfoDataService.update(sub, data)
.then((response) => {
console.log(response.data);
})
.catch((e) => {
console.error(e);
});
}
};
I thought userInDatabase could only give true or false, so I used it as an argument in the if statement. It does not work and just updates.
If you need any other information, please ask, I have just started and I hope I have given the necessary info.
EDIT
Through findOne I can find the entire object in my database, but I thought that putting the function as an if condition could give me true if it found the object with its sub; false if he found nothing.
This is not the case, in fact in the code I just updated, although findOne works correctly, it continues to execute always and only create.
I have just started to work with node.js and my js knowledge is a bit rusty.
I started with callbacks and then I found out about promises. This syntax was more like my brain works, so I rewrote my code with promises.
But now my MySql connection does not work. It can't even get reached (I think...)
I have logged the word "test" in the file userDB, that worked. But then in the mySql file the console.log "test2" didn't work.
Thanks to you all!
Chris
Here is my server.js (the file that node starts):
/* REQUIRE */
const oAuth2Server = require('node-oauth2-server');
const express = require('express');
const bodyParser = require('body-parser');
const oAuthModel = require('./endpoints/auth/authModel');
const util = require('util');
const dbCon = require('./subsystem/mySql')
/* CONST */
const port = 3000;
const debug = true;
const app = express();
/* INIT */
app.oauth = oAuth2Server({
model: oAuthModel,
grants: ['password'],
debug: debug
})
/* ROUTER */
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(app.oauth.errorHandler());
const authRoutes = require('./router/auth')(express.Router(), app, dbCon)
app.use('/auth', authRoutes);
app.all('*', (req, res) => {
res.status(404).send({message: "This service was not found"});
});
/* Start Server */
app.listen(port, () => {
console.log(`listening on port ${port}`)
})
Here is my router file auth.js:
module.exports = (router, expressApp, dbCon) => {
const userDB = require('../endpoints/user/userDB')(dbCon)
const authMiddleware = require('../endpoints/auth/authMiddleware')
const userMiddleware = require('../endpoints/user/userMiddleware')(userDB)
router.post('/registerUser', userMiddleware.registerUser)
//router.post('/login', expressApp.oauth.grant(), authMiddleware.login)
router.post('/login', expressApp.oauth.grant())
return router
}
Here is my userDB file:
let mySqlConnection;
module.exports = injectedMySqlConnection => {
mySqlConnection = injectedMySqlConnection
return {
registerUserInDB: registerUserInDB,
getUserFromCrentials: getUserFromCrentials,
doesUserExist: doesUserExist,
getUserByUsername: getUserByUsername
}
}
const registerUserInDB = (username, password) => {
return new Promise((resolve,reject) => {
//execute the query to register the user
mySqlConnection.query(`INSERT INTO users (username, user_password) VALUES ('${username}', SHA('${password}'))`)
.then(data => {resolve(true)})
.catch(error => {reject(error)})
})
}
const getUserFromCrentials = (username, password) => {
return new Promise((resolve,reject) => {
//create query using the data in the req.body to register the user in the db
const getUserQuery = `SELECT * FROM users WHERE username = '${username}' AND user_password = SHA('${password}')`
console.log('getUserFromCrentials query is: ', getUserQuery);
//execute the query to get the user
mySqlConnection.query(getUserQuery)
.then(data => {resolve(data)})
.catch(error => {reject(error)})
})
}
const doesUserExist = username => {
return new Promise((resolve,reject) => {
console.log('test');
//execute the query to check if the user exists
mySqlConnection.query(`SELECT * FROM users WHERE username = '${username}'`)
.then(data => {resolve(data.results !== null ? data.results.length == 1 ? true : false : false)})
.catch(error => {reject(error)})
})
}
const getUserByUsername = username => {
return new Promise((resolve,reject) => {
//execute the query to check if the user exists
mySqlConnection.query(`SELECT id, username FROM users WHERE username = '${username}'`)
.then(data => {resolve(data)})
.catch(error => {reject(error)})
})
}
Here is my userMiddleware (is this middleware?):
let userDb;
module.exports = injectedUserDb => {
userDb = injectedUserDb
return {
registerUser: registerUser
}
}
function registerUser(req, res){
console.log(`authRoutesMethods: registerUser: req.body is:`, req.body);
//query db to see if the user exists already
userDb.doesUserExist(req.body.username)
.then(data => {
if(data)
{
sendResponse(res, "User already exists", 200)
return
}
else
{
//register the user in the db
userDb.registerUserInDB(req.body.username, req.body.password)
.then(data => {
userDb.getUserByUsername(req.body.username)
.then(data => {sendResponse(res, data.results, "")})
.catch(error => {sendResponse(res, "Failed to register user", error)})
})
.catch(error => {sendResponse(res, "Failed to register user", error)})
}
})
.catch(err => {
sendResponse(res, "User already exists", 200)
return
})
}
function sendResponse(res, message, error) {
res
.status(error !== null ? error !== null ? 400 : 200 : 400)
.json({
'message': message,
'error': error,
})
}
And last but not least my mySql.js file:
var mySql = require('mysql');
const query = sql => {
return new Promise( ( resolve, reject ) => {
let connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'oauth2test'
});
console.log('test2');
connection.query( sql, ( err, rows ) => {
if ( err )
{
connection.end();
reject( err );
}
else
{
connection.end();
resolve( rows );
}
});
});
}
module.exports.query = query;
You have a simple typo in your mySql.js file:
The line
var mySql = require('mysql');
should be replaced with
var mysql = require('mysql');
Other than that the query code works ok on my machine:
var mysql = require('mysql');
const query = sql => {
return new Promise( ( resolve, reject ) => {
let connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'oauth2test'
});
console.log('test2');
connection.query( sql, ( err, rows ) => {
if ( err )
{
connection.end();
reject( err );
}
else
{
connection.end();
resolve( rows );
}
});
});
}
module.exports.query = query;
I have the following Node/Express route which is used to post data to a MySQL server. It first adds a new user to a table and then takes the id of the new user and adds some more info to a profile table. The second query is dependent on the first one so they run sequentially.
I have written the following code and it runs correctly and does the job.
routes.post('/register', (req,res) => {
console.log('api req: ', req.body)
const email = req.body.email
const password = 'test'// req.body.password
if (!email || !password) return res.status(400).json({type: 'error', message: 'Please provide email and password'})
const hash = bcrypt.hash(password, 10)
// console.log('hash is ...', hash )
var sqlquery = "INSERT INTO user (username, first_name, last_name, email, password) VALUES ('test#gmail.com', 'Dan', 'Brown', 'test#gmail.com', 'test')"
db.query(sqlquery, (error, results) => {
if (error) return res.status(400).json({type: 'error', message: error})
if (results.length == 0) {
// do something
} else {
// run another query based on results from previous query
var profilequery = "INSERT INTO userprofile (user_id, address, age) VALUES (" + results.insertId + ", 'test address', 25)"
db.query(profilequery, (error1, results1) => {
if (error) return res.status(400).json({type: 'error1', message: error1})
console.log("profile inserted, ID: " + results1)
})
}
console.log("1 record inserted, ID: " + results.insertId);
res.json({type: 'success', message: 'user registered', results})
return results
})
})
There are two problems:
Problem 1: This code is not asynchronous. I would love to use async/await on this code. Really appreciate if someone can help me convert this into async code.
Problem 2: I have tried to use bcrypt to hash the password. However, if I use the hashed value in the query, the query fails because bcrypt returns a promise and not the actual hashed password. I do I resolve this.
not tested:
asyncQuery = (query, args) => {
return new Promise((resolve, reject) => {
db.query(query, function (err, result, fields) {
if (err)
return reject(err);
resolve(result);
});
});
}
routes.post('/register', (req, res) => {
console.log('api req: ', req.body)
const email = req.body.email
const password = 'test'// req.body.password
if (!email || !password) return res.status(400).json({ type: 'error', message: 'Please provide email and password' })
const hash = bcrypt.hash(password, 10)
// console.log('hash is ...', hash )
const sqlquery = "INSERT INTO user (username, first_name, last_name, email, password) VALUES ('test#gmail.com', 'Dan', 'Brown', 'test#gmail.com', 'test')"
let firstresult, secondresult;
asyncQuery(sqlquery)
.then(rows => {
firstresult = rows;
const profilequery = `INSERT INTO userprofile (user_id, address, age) VALUES ("${rows.insertId}", 'test address', 25)`;
return asyncQuery(profilequery)
})
.then(rows => {
secondresult = rows;
})
.then( () => {
console.log(`firstresult:${firstresult}`)
console.log(`firstresult:${secondresult}`)
res.json({ type: 'success', message: 'user registered', results })
})
.catch(rows => {
console.log(`Error:${rows}`)
})
})