Access variable outside mongoose find method - node js - javascript

In my node js program, I look into my mongoose database, and find and return the values in that collection - there is only one value.
var myValueX;
myCollection.find(function(err, post) {
if (err) {
console.log('Error ' + err)
} else {
myValueX = post[0].valuex;
}
});
console.log('Have access here' + myValueX);
Now, I want to be able to use myValueX outside this find method. How can I do this?
When I try the console.log above, I get undefined back - is this possible to achieve

To access myValueX after it had been assigned in find's callback, you have two options, the first (naturally) is inside the callback itself, or inside a function called by the callback to which you send myValueX as an argument.
The better solution in my opinion is to use promises.
A simple promise-using solution is as follows:
function findPromise(collection) {
return new Promise((resovle, reject) => {
collection.find((err, post) => {
if (err)
return reject(err)
// if all you want from post is valuex
// otherwise you can send the whole post
resolve(post[0].valuex)
})
})
}
findPromise(collection)
.then((valueX) => {
// you can access valuex here
// and manipulate it anyway you want
// after it's been sent from the `findPromise`
console.log("valueX: ", valueX)
})
.catch((err) => {
console.log("An error occurred. Error: ", err)
})

Related

How to handle error in ldapjs search method

I must connect to a LDAP server and find user based on a specific ID.
I've decided to use ldapjs module.
I managed to create my client and bind him so I can search for an user with success.
My problem is, as I only use async/await, that I don't understant how to handle error in callbacks... For example with this simple code from ldapjs library :
public static async search(searchOptions: SearchOptions) {
LdapService.bind()
LdapService.getClient()?.search('ou=*****,ou=***,dc=****,dc=****', searchOptions, function (err, res) {
ifError(err)
res.on('searchEntry', function (entry) {
// ----------------------
// an error came from here
throw new Error('test')
// ----------------------
console.log('entry: ' + JSON.stringify(entry.object));
});
res.on('searchReference', function (referral) {
console.log('referral: ' + referral.uris.join());
});
res.on('error', function (err) {
console.error('error: ' + err.message);
});
res.on('end', function (result) {
console.log('status: ' + result?.status);
});
})
}
LdapService.getClient() is a singleton method that return the result of createClient -> works fine
LdapService.bind() is a method that just bind with the server with correct credentials -> works fine
I just can't manage to handle my error "test"... How am I supposed to handle it?
Is the search method really async?
Can I do it the async/await way? :P
PS: the DN string ("ou=,ou=,dc=,dc=") is hidden due to security reasons and the code works great without throwing an error ;)
For anyone passing here and struggling with callback like me, here the "fix" I found :
public static async search(searchOptions: SearchOptions) {
// wrapping the all thing in a Promise that can be in a try/catch -> error will be handled there
return await new Promise((resolve, reject) => {
LdapService.getClient()?.search('ou=****,ou=****,dc=****,dc=****', searchOptions, function (err, res) {
if (err) {
// handle connection error I guess ?
reject(err)
}
res.on('searchEntry', function (entry) {
try {
// -------------
// Same test as before
throw new Error('test')
// -------------
resolve(JSON.stringify(entry.object))
} catch (err) {
// error must be catched to call reject method !
reject(err)
}
});
res.on('error', function (err) {
// handle API error when looking for the specific ID I guess ?
reject(err)
});
})
})
}
What solved my problem? Wrapping the all thing in a Promise.
All the "res.on" methods before were something like Promise listeners. So the all method search (the one inside mine) was a sort of asynchronous call.
Now I can call resolve/reject methods to return data and error.
Also, I can call my static search method the async/await way.
When you're not familiar with callbacks... ^^"

res.redirect causes error in .catch statement

I am using Node.js and have a database call being performed with a promise. I setup a '.then/.catch' to handle the result of the promise. That part all seems to work without any issue. After I do some processing of the data returned from the database I am attempting to 'redirect' to another route within the '.then' statement. This re-direction seems to be causing a problem with the '.catch' statement. I have no error issues if I remove the 'res.redirect'...is there a workaround? Any suggestions appreciated.
code:
const db = require('./routes/queries');
//**Query PostGres database...
db.getMembers()
.then(function(value) {
console.log('Async success!', value);
//do some processing on the data returned from the promise call here...
//if I insert a redirect here...such as "res.redirect('/_members');"...
//I get a "Caught an error! ReferenceError: res is not defined" message...?
//evidently the redirect fires the '.catch' statement below...why?
})
.catch(function(err) {
console.log('Caught an error!', err);
});
I recommend providing a reusable promise in case you need to find your members list elsewhere in your Express app
const db = require('./routes/queries');
const findMembers = () => {
return db.getMembers()
.then(members => {
return members
})
.catch(err => {
console.log('Caught an error!', err);
});
}
//inside your express app
app.get("/some_route", (req, res) => {
return findMembers()
.then(members => {
//do something with your members list
res.redirect("/redirect_route")
})
})

Function returning undefined when callback is not used, on using callback, JS says function not defined

Please take into consideration that similar questions have been asked on SO and I went through most of them.
I am making a RESTful service that needs querying the DB to get the data. I wrote the code that queries the database correctly but does returns undefined all the time. The code is here:
function returnAll(){
ModuleDBService.find({},function(err,data){
if(err){
console.log('Error occured while retrieving the documents!');
}
return data;
});
}
I was exporting the module using:
module.exports = {
getAll:returnAll
};
After digging SO a lot, I discovered that I will need to use callback to get the data. I went through many examples and tried to apply a similar technique to my code, the modified code looked like this:
function getAllFromDatabase(callback){
ModuleDBService.find({},function(err,data){
if(err){
console.log('Error occured while retrieving the documents!');
}
callback(returnAll(data));
});
}
function returnAll(data){ return data;}
and then returning it in the similar fashion as above.
But now I am getting error that ModuleDAO.getAll is not a function (I am using var ModuleDAO = require('path to the database service').
I tried many variations of the code, went through a couple of videos on YouTube, all of them either lead to returning undefined, or return to the above stated error. If anyone could fix the code and throw light on this whole callback thing (Or could provide solid documentation to understand it), it'll be a great help.
Thanks.
EDIT: After all the extremely helpful answers, here is a summary:
Callbacks cannot return data, pass the function (the callback function) that you want your program to call with the data. In my case, it was my router returning the data.
Here is the corrected code:
function returnAll(callback) {
ModuleDBService.find({}, function (err, data) {
if (err) {
console.log("Error while retrieving the document!")
callback(null);
}
callback(data);
});
}
I used this code in my router as:
mainAPIRouter.post('/api/module', function (req, res) {
try {
moduleDAO.getAll(function(data){
res.status(200);
res.json(data);
});
} catch (error) {
res.status(500);
return res.send("Invalid request");
}
});
Thanks to all those who helped! :)
You are close. You don't need the returnAll() function, and you need to export getAllFromDatabase and pass a callback to it:
function getAllFromDatabase(callback){
ModuleDBService.find({},function(err,data){
if(err) {
console.log('Error occured while retrieving the documents!');
}
callback(data);
});
}
module.exports = {
getAllFromDatabase: getAllFromDatabase
};
Then, when you want to use it, you need a callback function:
dataModule.getAllFromDatabase(callbackHandler);
function callbackHandler(dataFromDatabase) {
// this function will be executed when ModuleDBService executes the callback
console.log(dataFromDatabase);
}
A small detail: if err is Truthy, you should not execute the callback:
if(err) {
console.log('Error occured while retrieving the documents!');
} else {
callback(data);
}
You want to simply call the callback() with the data you need as an argument. You are making things much more complicated by passing another function into the callback. Try something like:
function returnAll(callback) {
ModuleDBService.find({}, function(err, data) {
if (err) return callback(err)
callback(null, data);
});
}
returnAll(function(err, data)) {
// it's customary for callbacks to take an error as their first argument
if (err) {
console.log('Error occured while retrieving the documents!');
} else {
// use data here!!
}
}
As previous answers mentioned, you can use a callback. You can also use a promise if you prefer:
function returnAll(){
return new Promise(function(resolve, reject) {
ModuleDBService.find({},function(err,data){
if(err){
console.log('Error occured while retrieving the documents!');
reject(err);
}
resolve(data);
});
});
}
You would then use something like this to access it:
returnAll()
.then(data=> {console.log(data); })
.catch(err=> { console.log(err); });
*Edit: since you want to use a callback, thought I'd add my $0.02 there as well. The most streamlined approach would be to just use the callback you're passing in with the ModuleDBService.find, without the ad-hoc function. But it's good to verify that callback actually is a function, and if not to make it one...makes your code more fault-tolerant.
function returnAll(cb){
if(typeof cb!=='function') cb = function() {};
ModuleDBService.find({},cb);
}

Meteor Method callback getting undefined for result

I haven't coded in Meteor in a while, but I have this Meteor method that creates a task and returns the ID and another that appends that task to a project:
Meteor.methods({
createTask(task) {
// TODO Add a check here to validate
Tasks.insert(task, (err, id) => {
if (err) {
throw new Meteor.Error(err);
}
id = {id: id};
console.log('Returning id: ', id);
return id;
});
}
});
Meteor.methods({
appendTaskToProject(projectId, taskId) {
// TODO Add check here to validate
const project = Projects.findOne({_id: projectId});
if (project) {
if (!project.tasks) {
project.tasks = [];
}
project.tasks.push(taskId);
Projects.update({_id: projectId}, project, (err, id) => {
if (err) {
throw new Meteor.Error(err);
}
});
} else {
throw new Error("Could not find project");
}
}
});
I am attempting to call it on the client like thus:
Meteor.call('createTask', task, (err, taskId) => {
console.log('err: ', err);
console.log('taskId: ', taskId);
if (err) {
this.setState({ error: err.message});
} else {
Meteor.call('appendTaskToProject', projectId, taskId, (err, result) => {
if (err) {
this.setState({ error: err.message});
} else {
this.setState({ newTaskDialogOpen: false })
}
});
}
});
The problem I am having is that taskId is not set in the callback. From the method-side I see the log message inthe server like:
I20180110-07:30:46.211(-5)? Returning id: { id: 'a3nS9GcRhuhhLiseb' }
And in the client:
Returning id: {id: "a3nS9GcRhuhhLiseb"}id:
Tasks.jsx:43 err: undefined
Tasks.jsx:44 taskId: undefined
So I know it's returning something, but the callback is just not getting it. I know I should probably change the createTask to just take the task AND the projectId to link it too, but I would like to try and figure out why it's not getting the results of the Meteor method into the callback on the client-side.
The Meteor API documentation on collection methods like insert says the following:
On the server, if you don’t provide a callback, then insert blocks
until the database acknowledges the write, or throws an exception if
something went wrong. If you do provide a callback, insert still
returns the ID immediately. Once the insert completes (or fails), the
callback is called with error and result arguments. In an error case,
result is undefined. If the insert is successful, error is undefined
and result is the new document ID.
Applying this information to your code would create the following:
Meteor.methods({
createTask(task) {
// TODO Add a check here to validate
return Tasks.insert(task, (err, id) => {
if (err) {
throw new Meteor.Error(err);
}
});
}
});
This would return the new generated ID immediately but will have the disadvantage, that the error is thrown afterwards. Therefore you better be going the direct way and execute "sync-like":
Meteor.methods({
createTask(task) {
// TODO Add a check here to validate
return Tasks.insert(task);
}
});
The meteor method automatically wraps the code so that on a positive return your client will receive a null value for error and the _id value for result. If an error occurs during the insert, that method will automatically return the error in the client callback as error and the reuslt will be null.
If you are concered with the synchronous nature of the code read this part of the guide about methhods.
Same should apply for your update method:
Meteor.methods({
appendTaskToProject(projectId, taskId) {
// TODO Add check here to validate
return Projects.update({_id: projectId}, {$push: {tasks: taskId});
}
});
Note, that I summarized this method to a more mongo oriented approach.
You need to return the id outside of the insert callback.
Meteor.methods({
createTask(task) {
// TODO Add a check here to validate
var returnID;
Tasks.insert(task, (err, id) => {
if (err) {
throw new Meteor.Error(err);
}
id = {id: id};
returnID = id;
console.log('Returning id: ', id);
// return id; --not here
});
return returnID; //return it here.
}
});
Possible explanations can be found here.

Promise code are read twice

I use the following code to read json file and return a promise
I've two questions
return globAsync("folder/*.json").catch(function (err) {
throw new Error("Error read: " + err);
}).map(function (file) {
return fs.readFileAsync(file, 'utf8')
.then(function (res) {
console.log("test");
return JSON.parse(res);
},
function (err) {
throw new Error("Error :" + err);
}).then(function () {
console.log("test2");
});
});
I use the console log and I see that the console is printed twice
test
test
test2
test2
why its happening and how to avoid it ?
In the place I've put console.log("test2"); I need to invoke event
that the json parse is finished and still return outside the json object (to the caller), when I add the last then it doesn't work(the returned object is undefined),any idea how to do that right?
UPDATE I try like following which it doesn't work...
return globAsync("folder/*.json").catch(function (err) {
throw new Error("Error read: " + err);
}).map(function (file) {
return fs.readFileAsync(file, 'utf8')
.then(function (res) {
console.log("test");
JSON.parse(res); //data parse
}.catch(function (err) {
throw new Error("Error :" + err);
}
).then(function (data) {
obj.emit('ready');
return data;
}))
});
}
UPDATE2 I was able to solve it by simply add new return JSON.parse(res);
Now how should I solve the first issue which method called twice
Like #jaromandaX said, you probably got two *.json files. Try to print out the file name instead and it should become more obvious. In that case, .map is expected to be called twice, once for each file. Otherwise you aren't gonna be able to read and parse two files together.
If you want to get it to converge to a single point after all file reads and parses are complete, then you need to chain another .then after .map. eg.
return globAsync("folder/*.json")
.map(function(file) {
...
})
.then(function() {
obj.emit('ready');
});
EDIT To answer your question in comment. There are a few things you should keep in mind.
Throwing Error inside the promise chain will get caught by the promise and send it into the rejection flow. You may still throw an error if you are interested in getting custom error type or printing stack trace in a desirable way. But most people prefer return Promise.reject(error).
Any rejection in .map will send the promise chain into rejection flow.
Inside the rejection chain, if you want to continue down the rejection flow. You need to return Promise.reject(error), otherwise if you don't return a reject object, you can bring it back into resolve flow.
If you want to want to handle each error individually, you can do something like this:
return globAsync("folder/*.json")
.catch(function(error) {
// TODO: Handle error
return Promise.reject(error);
})
.map(function(file) {
return fs.readFileAsync(file, 'utf8')
.catch(function(error) {
// TODO: Handle error
return Promise.reject(error);
})
.then(function(res) {
return JSON.parse(res);
});
})
.then(function() {
obj.emit('ready');
});
If you want to handle once for glob and once for file read, then you have to get a bit more creative.
return globAsync("folder/*.json")
.catch(function(error) {
// TODO: Handle error
return Promise.reject(error);
})
.then(function(files) {
return Promise.resolve(files)
.map(function(file) {
return fs.readFileAsync(file, 'utf8');
})
.catch(function(error) {
// TODO: Handle error once for any read error
return Promise.reject(error);
})
.map(function(res) {
// Judging by your original code, you are not handling
// parser error, so I wrote this code to behave equivalent
// to your original. Otherwise chain parse immediate after
// readFileAsync.
return JSON.parse(res);
});
})
.then(function() {
obj.emit('ready');
});

Categories

Resources