Callback is not a function node.js, mongodb, res.render - javascript

I am trying to get data from a MongoDB collection so I created a database.js module to do this. One of the functions on the module was an exports.find function used to find data from a collection. I pass in a callback into the function because the Javascript is asynchronous and it takes time to retrieve data from the server. This is the code of the exports.find.
exports.find = (database, table, projection, callback) => {
MongoClient.connect(url, (err, db) => {
if (err) throw err
var dbo = db.db(database);
dbo.collection(table).find({}, projection).toArray(function(err, results) {
if (err) throw err
callback()
db.close()
});
});
}
I then use this function in my index.js file to use the found data and send it to the client. I did this by passing in res.render into my callback.
app.get('/', (req, res) => {
database.find('blog', 'blog-posts', {}, res.render('pages/index'))
})
Whenever I run this however, I get this error
TypeError: callback is not a function
at C:\Users\21Swag\Documents\Coding\Blog\database.js:23:13
The error points to the database.js file when I call the callback.
callback()
Does anyone know how to fix this? Any help would be appreciated.

Your callback is not a function, because you are already invoking it in database.find and then trying to call the return value of said function as your callback.
Maybe this will help:
const one = () => console.log(1)
const cbTest = (cb) => cb()
cbTest(one()) // Error: cb is not a function as it already returned 1
cbTest(one) // will console.log 1 as it is invoked correctly
Usually a callback is passed as an anonymous function like this:
database.find('blog', 'blog-posts', {}, () => res.render('pages/index'))
You can see the same pattern in the MongoClient.connect function/method. The second argument is the anonymous callback with (err, db) as parameters.

Related

How does http.createServer() know the parameters of the callback?

here is the picture
http.createServer((req,res)=>{});
how the createServer function knows that the callback function has 'req' for incoming message object and 'res' for server response object automatically
Can someone show me an example of how to create a function like createServer
http.createServer() has no idea what you declared for callback parameters. Regardless of how you declare your callback, http.createServer() passes two arguments to the callback when you call it. The first is the http request object, the second is the http response object. If your callback wants to work properly and use those arguments, it must create two arguments that match those. You can name them anything you want. The name is local only to your callback.
So, you can do any of these:
http.createServer((request, response) => {
response.end("hi");
});
http.createServer((req, res, goop) => {
// goop will be undefined here since no third argument is passed to the callback
res.end("hi");
});
http.createServer((a, b) => {
b.end("hi");
});
http.createServer((...args) => {
args[1].end("hi");
});
http.createServer(function() {
arguments[1].end("hi");
});
Can someone show me an example of how to create a function like createServer
You can create a function that accepts a callback function and then you call that callback with a set of arguments:
function readJSON(filename, callback) {
fs.readFile(filename, function(err, data) {
if (err) {
callback(err);
} else {
try {
let json = JSON.parse(data);
callback(null, json);
} catch(e) {
callback(e);
}
}
});
}
Here you create a function that takes a filename and a callback. It then reads that file, parses the JSON in it and calls the callback with null for the err and the parsed Javascript object for the 2nd parameter. If there's an error, then it just passes the error as the first argument.
HTTP is a class that contains 1 function called createServer, you can find some details on the HTTP object here https://nodejs.org/api/http.html, and when something like a request happens, the server returns your callback function with some parameters.
Hopefully, it will be easier to understand with 1 example:
You have the following class:
class User {
userInfo = {
name: "Jhon",
surname: "Doe"
}
getFullName = (callback) => {
return callback(this.userInfo.name, this.userInfo.surname)
}
}
This class has 2 defined properties, userInfo that contains the name and surname of the "user", and 1 function, getFullName, that function expects another function as is input, and whenever we call it, we will receive 2 parameters as output:
const UserInfo = new User()
UserInfo.getFullName((name,surname) => console.log(`Hello ${name} ${surname}`))
Hopefully, that answers your question, if you have any further questions or I completely misunderstood your question, feel free to tell me!

Pass extra variable to callback function

I am trying to create a simple method that logs out my DB operation results in a prettified format in the console. I want to pass an extra variable (operationName) to my custom logger callback function when my DB finishes an operation.
// Custom logger (simplified)
const dbLogger = (error, data, operationName) => {
if (error) {
console.log(`${operationName} failed`, error)
}
console.log(`${operationName} success`, data)
}
// mongodb operations
import ChatModel from 'somewhere';
function createMessage() {
const newChatAdded = new ChatModel({
message: 'Hi'
})
ChatModel.save(newChatAdded, dbLogger);
}
Here the error and data in our callback is provided by the ChatModel.save method but I want to send a 3rd parameter operationName (eg. 'save' or 'delete') as well.
Two ways, take your pick!
use anonymous function with a rest parameter and spread these on calling dbLogger:
ChatModel.save(newChatAdded, (...args) => dbLogger(...args, "save");
use a bound function:
Change your dbLogger function signature to accept operationName as the first argument:
const dbLogger = (operationName, error, data) => {
if (error) {
console.log(`${operationName} failed`, error)
}
console.log(`${operationName} success`, data)
}
Create new bound log function which will always be called with "save" as the first argument.
ChatModel.save(newChatAdded, dbLogger.bind(null, "save");
This is also called "partial application".
ChatModel.save(newChatAdded, (error, data) => dbLogger(error, data, 'save'));
If you'd change the order of your parameters, you could make that more succinct:
const dbLogger = (operationName, error, data) => ...
ChatModel.save(newChatAdded, dbLogger.bind(null, 'save'));

Using a Variable Out of Scope of a Callback Function in Node.js / Express.js

I am using a library called node-geocoder in express js which has the following code:
var NodeGeocoder = require('node-geocoder');
var options = {
provider: 'google',
// Optional depending on the providers
httpAdapter: 'https', // Default
apiKey: 'YOUR_API_KEY', // for Mapquest, OpenCage, Google Premier
formatter: null // 'gpx', 'string', ...
};
var geocoder = NodeGeocoder(options);
// Using callback
geocoder.geocode('29 champs elysée paris', function(err, res) {
console.log(res);
});
The response variable(res) in the geocode method's callback function holds an object with location properties such as latitutde and longitude. The link for this package is here
I was wondering if there was a way to use that response variable outside of the callback function in the geocode method. I need to pull the latitude and longitude properties and I don't want to keep the rest of the code within that callback function.
As a noob I tried just returning the object and storing it in a variable like so:
var object = geocoder.geocode('29 champs elysée paris', function(err, res) {
return res;
});
This doesn't work since it's in the callback and not returned in the actual geocode method.
Not directly, but there are a couple of options to get closer to that.
One would be to have your callback be a defined function and use that as the callback:
const doSomething = (err, res) => { /* do something */ }
geocoder.geocode('abc', doSomething);
Not really much different, but can make it a little cleaner.
You can also "promisify" the function to have it return a Promise. Something like this would do the trick:
const geocodePromise = (path) => new Promise((resolve, reject) => {
geocoder.geocode(path, (err, res) => err ? reject(err) : resolve(res));
});
geocodePromise('abc')
.then(res => { /* do something */ })
.catch(err => { /* do something */ });
Finally, if you are using Babel for transpiling (or Node version 7.6.0 or higher, which has it natively), you can use async and await. Using the same promisified version of the function as above, you'd have to wrap your main code in an async function. I generally use a self-calling anonymous function for that:
(async () => {
try {
const res = await geocodePromise(path);
/* do something with res */
} catch (err) {
/* do something with err */
}
})();
With this, you get the closest to what you want, but you'll still have to wrap your main code up in a function because you can't await at the top level.
I don't want to keep the rest of the code within that callback function.
The code doesn't have to be in the callback, it just has to be called from the callback.
So you write your function (or functions) as normal:
function handleInfo(info) {
doSomethingWithInfo(info);
doSomethignElse(info);
// ...
}
// ...
...and then call those functions when you have the data:
geocoder.geocode('29 champs elysée paris', function(err, info) {
if (err) {
// Handle error
} else {
handleInfo(info);
}
});
You can use the response of your function outside the callback by calling another function.
geocoder.geocode('29 champs elysée paris', function(err, res) {
if(!err){
// call a function if there is no error
console.log(res);
myFunction();
}
});
function myFunction(){
//do your processing here
}
geocode can return promise already, no need to re-wrap it. If you don't care about the error and just want to grab the response's location data. You can do this
var locationData = geocoder.geocode('29 champs elysée paris').then( function(res){
return res;
});

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.

My callbacks are wrong - I am returning my response to my error object - Nodejs

I am trying to learn node and understand how callbacks are working. I am doing this by trying to use the async lib. I am trying to hit my db with 2 separate calls and use the async lib to let me know when my object is ready to build. Here is my aysnc code:
async.parallel({
one: function(callback) {
getUser(id, callback);
},
two: function(callback) {
getUserServices(id, callback);
}
}, function(err, results) {
if(err) {
console.log(err);
new Error();
}
res.json(result);
});
Here are what my functions look like where I am calling the db. There are both basically the same function:
var getUser = function(id, callback) {
var query = client.query('SELECT * FROM USERS WHERE USER_ID=$1', [id]);
query.on('row', function(row, result) {
result.addRow(row);
});
query.on('end', function(result) {
callback(result);
});
};
My code hits the db and returns the user, but when it goes back up to the async code the user is in the err object. What am I doing wrong? How do I properly set up callbacks?
As pointed by damphat, your code should be
//additionally, handle errors
query.on('error', function(err){
callback(err) // The first argument to callback should be an error object
})
query.on('end', function(result){
callback(null, result) //passing null error object
})

Categories

Resources