callback is not a function (but it is) - javascript

Probably this will be an easy question because I don't understand the concept of callback (I thought I did, but apparently no).
I'm trying to return a response from MySQL (easy thing to do in almost every language but with nodeJS):
I'm calling a function from my routes file:
route
const user = require('../models/Users');
router.post('/register', (req, res, next) => {
let newUser = {
username: req.body.username,
password: req.body.password
};
if(user.checkIfUserExists) {
// do something
}
});
And here's the query inside another file:
const mysql = require('../handlers/mysql');
const bcrypt = require('bcrypt-nodejs');
var User = {
checkIfUserExists: function(newUser, callback) {
mysql.query('SELECT * FROM users where Email like ?', [newUser.username], function(err, result) {
if(err) return callback(err);
callback(null, result > 0);
});
},
registerUser: function(newUser) {
mysql.query("INSERT INTO users (Email, Password) VALUES (?, ?)", [newUser.username, User.hashPassword(newUser.password)], function(err, results) {
if (err) throw err;
return true;
});
},
hashPassword: function(password) {
let salt = bcrypt.genSaltSync(10);
return bcrypt.hashSync(password, salt);
}
}
module.exports = User;
Of course, same thing will happen with registerUser

You forget to call and handle the callback.
user.checkIfUserExists(newUser, function(arg1, isUserExist) {
if (isUserExist) {
//user exist
}
});

You are not using callback. To do that follow the below code.
`user.checkIfUserExists(newUser,(result)=>{
If(result){
//write your code
}
}`

Many guys have explained you problem. Let me explain you the concept of callback.
Callback is the function that is called, after a function has finished their job
For example, this function
setTimeout(function(){
alert('Alert will be ran after 3 seconds');
},3000);
You can open the developer mode (hit f12 on your browser and run it). You can see that the alert box will display after 3 seconds. I can explain the flow of code like this.
setTimeout function is run. This function will be finished after 3000 milliseconds, after that it will run the callback function
After 3000 milliseconds, the callback function is called. So the alert box is displayed.
In your code, you just create a plain new object, it does not have the checkIfUserExists function which is from the User object. You have to create user from the User class.
The callback function caused a lot of trouble to beginner, that's why in ES6+ (Javascript Version 6+). The class, async, await are introduced and make life easier.

Related

How to return the results of mySql query using express.js?

I am trying to get the results of my simple SELECT command to the index.js file, where I would like to have all records separated in a array. If I print the results in the database.js the JSON.parse just work fine. But if I want to return them and get them into the index.js where I need them, I always get undefined when I print it.
index.js CODE
const express = require('express');
const app = express();
const database = require('./database');
app.use(express.json());
app.use(express.urlencoded());
app.use(express.static('public'));
app.get('/form', (req,res) =>{
res.sendFile(__dirname + '/public/index.html' );
console.log(req.url);
console.log(req.path);
})
app.listen(4000, () =>{
console.log("Server listening on port 4000");
database.connection;
database.connected();
//console.log(database.select());
let results = [];
//results.push(database.select('username, password'));
let allPlayer = database.select('username');
console.log(allPlayer);
});
database.js CODE
let mysql = require('mysql');
const connection = mysql.createConnection({
host: 'localhost',
database: 'minigames',
user: 'root',
password: 'root'
});
function connected(){
connection.connect((err) => {
if(err) throw err;
console.log("Connected...");
})
}
function select(attribute){
let allPlayer = [];
let sql = `SELECT ${attribute} FROM player`;
let query = connection.query(sql, (err, result, field) => {
if(err) throw err;
return Object.values(JSON.parse(JSON.stringify(result)));
})
}
module.exports = {connection, connected, select};
Understand that one of the main things that make JavaScript different from other languages is that it's asynchronous, in simple terms meaning code doesn't "wait" for the code before it to finish executing. Because of this, when you're trying to query a database, which takes some time, the code after it gets impatient and executes regardless of how to the query is doing. To solve this problem, the mysql package utilizes callbacks, which allows you to pass a function to it to execute once the query is finished with the queries result.
Because the library operates on callbacks, it doesn't return anything; that seems quite problematic for using it somewhere else, doesn't it?
To solve this problem, we can make our own callback. Or better yet, use the newer JavaScript feature called promises, where you can basically "return" anything from a function, even when you're in a callback.
Let's implement it with the query:
function select(attribute) {
return new Promise((resolve, reject) => {
let sql = `SELECT ${attribute} FROM player`;
let query = connection.query(sql, (err, result, field) => {
if(err) return reject(err);
resolve(Object.values(JSON.parse(JSON.stringify(result))));
});
});
}
To "return" from a promise, we pass a value to the resolve function. To throw an error, we call the reject function with the error as the argument.
Our new function is rather easy to use.
select("abcd").then(result => {
console.log("Result received:", result);
}).catch(err => {
console.error("Oops...", err);
});
You might look at this code and go, "Wait a minute, we're still using callbacks. This doesn't solve my problem!"
Introducing async/await, a feature to let you work just with that. We can call the function instead like this:
// all 'await's must be wrapped in an 'async' function
async function query() {
const result = await select("abcd"); // woah, this new await keyword makes life so much easier!
console.log("Result received:", result);
}
query(); // yay!!
To implement error catching, you can wrap you stuff inside a try {...} catch {...} block.

javascript save function usage

I am learning about building a new user login app and there is a line of code the makes me quite confused and Google did not show me any enlightening result.
module.exports.createUser = function(newUser, callback){
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(newUser.password, salt, function(err, hash) {
newUser.password = hash;
newUser.save(callback);
});
});
}
It is quite straightforward: The new user module is trying to crypt the password and runs the callback(It is my understanding). But this line here:
newUser.save(callback);
It is very confusing to me and would anyone explain what it does here?
Thank you
You pass a callback function as a parameter to the outer most function. And when the genSalt- and hash-functions are done, you pass the same callback function as a parameter to newUser.save(). It is a function that (probably) will be executed inside newUser.save().
Sometimes using callbacks are a way to say that some code has completed, and then run a new function. Useful for async funcions.
A simpler example:
serveCake = function(){ // this is my callback function
// serve the cake
}
createCake = function(ingredients, callback){ // pass serveCake as a callback here.
// Mix the ingredients and put the cake in the oven
// When the cake is finished baking, serve it
callback(); // call the callback function.
}
const ingredients = ['suger', 'butter'];
createCake(ingredients,serveCake);
The same happens with your code, but the callback function will (probably) be executed inside the newUser.save()-method.
Mongoose save method expects an optional callback function as an argument (Reference Doc). So when you are calling newUser.save(callback); you are passing the same callback function that you got in the createUser(newUser, callback); so whichever method will call this method i.e. suppose in following code callIt() function is calling your code.
module.exports.createUser = (newUser, callback) => {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
newUser.password = hash;
newUser.save(callback);
});
});
};
function callIt() {
module.exports.createUser({ name: 'ridham' }, function (err, user) {
if (err) {
console.log(error);
} else {
console.log(user);
}
});
}
callIt();
so here you are giving function (err, user) {...} function as an argument which can be called by invoked function to pass back the access to invoking the function(handling async nature of JS). and your invoke function again invoking another function passing the same callback reference so basically, you are chaining the same callback.
Callback Explanations can be found here and here.

How do we use Async, and what is the best way in nodeJS

I'm trying to pass a query to my database, then send the result to my client side, but it looks like the request is async, because my request happens after that my post request returns the value.
How do I set an await for request?
My database connection
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'my_password',
database: 'MEETME'
});
connection.connect(function(err) {
if (err) {
console.log("error while connecting to database");
console.log(err.code);
}
});
// function that query database <-------
function queryDatabase(userQuery) {
connection.query(userQuery, function(err, result) {
if (err) {
throw err;
}
console.log("before");
return result;
});
}
and this is my post request
//POST
app.post('/signin', function(request, response) {
var query = queryDatabase("SELECT EMAIL FROM MEMBER WHERE ID_MEMBER = 3");
console.log(query);
console.log("after");
response.end(query, 200);
});
the result in the console is:
undefined
after
before
Change the implementation of queryDatabase function to return a promise. Any function that returns a promise can be awaited.
function queryDatabase(userQuery){
return new Promise((resolve,reject) => {
connection.query(userQuery, function(err, result){
if(err){
reject(err);
}
console.log("before");
resolve(result);
});
});
}
app.post('/signin', async function(request, response){
var query = await queryDatabase("SELECT EMAIL FROM MEMBER WHERE ID_MEMBER = 3");
console.log(query);
console.log("after");
response.end(query, 200);
});
Welcome to Node.js where everything is intended to be asynchronous and unless you explicitly structure your code in a way that cascades one event to another there's no obligation on the part of the code to run in any particular order at all.
I'd strongly recommend picking up on Promises as a way of organizing code if you're not familiar with this concept. This wraps up a lot of tricky programming into some neat, tidy methods, and makes chaining, fan-out and fan-in actually pretty simple.
For example, rewritten with Sequelize, a database layer that uses promises:
function queryDatabase(userQuery){
console.log("before");
return connection.query(userQuery);
}
You return the promise, and that's used to chain. If you don't you must accept a callback argument and chain that through. Return values are largely ignored:
function queryDatabase(userQuery, cb){
connection.query(userQuery, function(err, result){
cb(err, result);
});
console.log("before");
}
You can see there's a lot more cruft already, and even more if you needed to build off of that. Inserting optional steps in callback driven code is tricky.
Promises make your code end up looking like this:
app.post('/signin', function(request, response){
queryDatabase("SELECT EMAIL FROM MEMBER WHERE ID_MEMBER = 3")
.then(function(results) {
console.log(results);
console.log("after");
response.end(query, 200);
});
});
It's also trivial to patch in error handling in one place with catch to handle errors.

Mongodb find() returns undefined (node.js)

Ive been playing around with mongodb in node.js. I have made a basic collection with some data (i know its there ive checked). When I try to run a find() on the collection it returns undefined. I dont know why this is. The code is below:
function get_accounts(){
var MongoClient = mongodb.MongoClient;
var url = "url";
MongoClient.connect(url, function (err, db) {
if (err) {
console.log('Unable to connect to the mongoDB server. Error:', err);
} else {
//HURRAY!! We are connected. :)
console.log('Connection established to database');
var collection = db.collection('accounts');
collection.find().toArray(function(err, docs) {
console.log("Printing docs from Array")
docs.forEach(function(doc) {
console.log("Doc from Array ");
console.dir(doc);
});
});
console.log("mission complete");
}
db.close();
}
);
}
If you know why this is happening i would like to hear your thoughts. thanks! The database is a mongolab hosted database if that makes any difference.
You are getting an undefined value because of the asynchronous nature of node.js, nowhere in your code exists logic that tells the console.log statement to wait until the find() statement finishes before it prints out the documents. You have to understand the concept of callbacks in Node.js. There are a few problems here, though, that you could fix. A lot of people getting started with node have the tendency to nest lots of anonymous functions, creating the dreaded "pyramid of doom" or callback hell. By breaking out some functions and naming them, you can make it a lot cleaner and easier to follow:
var MongoClient = require("mongodb").MongoClient
// move connecting to mongo logic into a function to avoid the "pyramid of doom"
function getConnection(cb) {
MongoClient.connect("your-mongo-url", function(err, db) {
if (err) return cb(err);
var accounts = db.collection("accounts");
cb(null, accounts);
})
}
// list all of the documents by passing an empty selector.
// This returns a 'cursor' which allows you to walk through the documents
function readAll(collection, cb) {
collection.find({}, cb);
}
function printAccount(account) {
// make sure you found your account!
if (!account) {
console.log("Couldn't find the account you asked for!");
}
console.log("Account from Array "+ account);
}
// the each method allows you to walk through the result set,
// notice the callback, as every time the callback
// is called, there is another chance of an error
function printAccounts(accounts, cb) {
accounts.each(function(err, account) {
if (err) return cb(err);
printAccount(account);
});
}
function get_accounts(cb) {
getConnection(function(err, collection) {
if (err) return cb(err);
// need to make sure to close the database, otherwise the process
// won't stop
function processAccounts(err, accounts) {
if (err) return cb(err);
// the callback to each is called for every result,
// once it returns a null, you know
// the result set is done
accounts.each(function(err, account) {
if (err) return cb(err)
if (hero) {
printAccount(account);
} else {
collection.db.close();
cb();
}
})
}
readAll(collection, processAccounts);
})
}
// Call the get_accounts function
get_accounts(function(err) {
if (err) {
console.log("had an error!", err);
process.exit(1);
}
});
You might have to add an empty JSON object inside the find.
collection.find({})
Documentation can be found here.
You must enter this code in an async function and you will be fine here data is the your desired value and you must use promises to not make your code look messy.
var accountCollection = db.collection('accounts);
let data = await accountCollection.find().toArray.then(data=>data).catch(err=>err);

Passing a function result to a parent function [duplicate]

This question already has answers here:
How to return Mongoose results from the find method?
(6 answers)
Closed 9 years ago.
So I have this method for a User object to check for an existing record.
User.findOrCreate = function(json){
User.findOne({'email' : json.email}, function(err, user){
if (!user){
user = new User({
id: json.id,
email: json.email,
f_name: json.first_name,
l_name: json.last_name,
gender: json.gender,
locale: json.locale
});
user.save(function(err){
if (err) return handleError(err);
return user;
});
} else {
return user;
}
});
}
so basically I want to return the variable "user" to the method findOrCreate, so that I can use it in another place like so:
var user = User.findOrCreate(profile._json);
But instead, user = undefined when I log it out. Having a lot of trouble with this. Can anyone help?
You can't do this:
var user = User.findOrCreate(profile._json);
Because the execution of User.findOne is async, so when findOrCreate returns, probably User.findOne wasn't executed yet.
The right way of getting the user value is to use another callback in findOrCreate:
User.findOrCreate = function(json, callback){
User.findOne({'email' : json.email}, function(err, user){
if (!user){
user = new User({
id: json.id,
email: json.email,
f_name: json.first_name,
l_name: json.last_name,
gender: json.gender,
locale: json.locale
});
user.save(function(err){
if (err) return handleError(err);
callback(user);
});
} else {
callback(user);
}
});
};
As you can see handling all of those callbacks can make you mad. If you didn't tried promises yet take a look to the Q library: http://documentup.com/kriskowal/q/
It will make your life easy:
var userPromise = User.findOrCreate(profile._json);
/* then when you need a user */
userPromise.done(function (user) { /* do something */ });
The advantage is that promises comes with very useful abstractions to handle control flow of async code, for example using promises you can write findOrCreate like this:
User.findOrCreate = function(json) {
return User.findOne({email:json.email}).then(createIfNull);
};
Where createIfNull is the code that you have inside the if block (if save also returns a promise things are easy.. if not you can create one and return it, see Q examples... the docs are not very intuitive but once that you get used to it you'll see that it really simplifies all the callback mess).
The User.getOrCreate function has not any return statement, so of course it will return undefined, in fact findOne is working async, so your getOrCreate function should be async too, So you can change your function definition to:
User.findOrCreate = function(json, callback) {
and instead of return user; you should run the callback function:
callback(user);

Categories

Resources