I am trying to write a function that accepts the mongodb collection name as the paramater and return an instance of the collection so that it can be used to perform CRUD operartions. But when I am trying to return the instance of the collection it returns 'undefined' as the return statement is executed before the MongoClient.connect function finishes its execution.
module.exports.dbConnection = function(collectionName)
{
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://127.0.0.1/test";
var collName;
MongoClient.connect(url, function(err, db)
{
var collName = db.collection(collectionName);
console.log(collName)
});
return collName;
}
Can I get assitance on how I can go about solving this issue.
Thanks
If you are using at least version 7.10 of Node you can accomplish this using async functions and promises.
// You can return a promise and resolve the promise once connected
module.exports.dbConnection = function dbConnection(collectionName) {
const MongoClient = require('mongodb').MongoClient;
const url = "mongodb://127.0.0.1/test";
return new Promise((resolve, reject) => {
MongoClient.connect(url, function (err, db) {
if (err) {
return reject(err);
}
resolve(db.collection(collectionName));
});
});
}
// You can then call the function within an async function (Node v7.10 and above)
async function fnThatCallsDbConnection() {
try {
const collName = await dbConnection('someCollection');
} catch(e){
// do something with error
}
}
Something additional you can do is cache your database connection so you don't need to connect every time - here's a way you could do it:
let cachedDB;
module.exports.dbConnection = function dbConnection(collectionName) {
const MongoClient = require('mongodb').MongoClient;
const url = "mongodb://127.0.0.1/test";
return new Promise((resolve, reject) => {
if (cachedDB) {
resolve(cachedDB.collection(collectionName));
} else {
MongoClient.connect(url, function (err, db) {
if (err) {
return reject(err);
}
cachedDB = db;
resolve(db.collection(collectionName));
});
}
});
}
The proper way to accomplish this is to use a callback. Accept a callback argument, and then pass your desired information to that function when the operation is complete.
module.exports.dbConnection = function(collectionName, cb)
{
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://127.0.0.1/test";
var collName;
MongoClient.connect(url, function(err, db)
{
var collName = db.collection(collectionName);
cb(collName); // invoke our callback
});
}
And you use it like this:
dbConnection('collName', function (coll) {
console.log(coll);
// do something else with our collection
})
Related
I saw somewhere to do something you need to do something with db.eval, but node throw error that eval is not a function. I wanna execute string as mongo query. It DYNAMIC
const mongoClient = require("mongodb");
const url = "someurl";
const mongoClient = new MongoClient(url)
async function run() {
try {
await mongoClient.connect();
const db = mongoClient.db("somedb");
var query = "db.getCollection('somecollection').find().limit(1)";
db.eval('function(){ return ' + query + '.toArray(); }', function (err, res) {
console.log(res);
});
}catch(err) {
console.log(err);
} finally {
await mongoClient.close();
}
}
run();
I'm not capturing the connect function to return in the getAllowedEmails function, when I do console.log in allowedEmails, it returns the emails correctly, but when I assign to the variable emails, it is returning empty. I think it's an async await problem but can't figured out.
static async getAllowedEmails() {
var MongoClient = require("mongodb").MongoClient;
//db url
let emails = [];
await MongoClient.connect(url, async function (err, client) {
const db = client.db("data-admin");
var myPromise = () => {
return new Promise((resolve, reject) => {
db.collection("users")
.find({})
.toArray(function (err, data) {
err ? reject(err) : resolve(data);
});
});
};
var result = await myPromise();
client.close();
let allowedEmails = [];
result.map((email) => allowedEmails.push(email.email));
console.log(allowedEmails)
emails = allowedEmails;
});
console.log(emails)
return emails;
}
Your code has couple of issues, I have fixed few and enhanced it, given below is the basic code, try to test it and if everything works then enhance it as needed:
const MongoClient = require("mongodb").MongoClient;
async function getAllowedEmails() {
let client;
try {
const allowedEmails = [];
client = await MongoClient.connect(url);
const db = client.db("data-admin");
const result = await db.collection("users").find({}).toArray();
result.map((email) => allowedEmails.push(email.email));
console.log(allowedEmails)
client.close();
return allowedEmails;
} catch (error) {
(client) && client.close();
console.log(error)
throw error;
}
}
I have a code to insert user data to the database like in following;
exports.signup = function (data) {
const mongodb = require('mongodb').MongoClient;
var rcode;
const url = "mongodb://localhost:27017";
mongodb.connect(url,(err,db) => {
if(err) rcode = 24
var dbo = db.db('project');
dbo.collection('users').insertOne(data, (err, dres) => {
if (err) rcode = 24
else rcode = 25
db.close;
})
})
console.log(rcode) // It returns 'undefined'
return rcode;
}
I try to return a number about the success of the process. But I get the result of 'undefined' every time. How can I solve this issue most accurately?
-- I've comprehended the root of the issue, that's asynchronous but I couldn't find a way to get that number. Can you give me a short example and explanation?
mongodb.connect and dbo.collection('users').insertOne are async calls.
You need to use promise to handle it.
Approach 1: new Promise((resolve, reject) => {})
exports.signup = function (data) {
const mongodb = require('mongodb').MongoClient;
var rcode;
const url = "mongodb://localhost:27017";
return new Promise((resolve, reject) => {
mongodb.connect(url,(err,db) => {
if(err) resolve(24) // here you can use reject('error')
var dbo = db.db('project');
dbo.collection('users').insertOne(data, (err, dres) => {
if (err) rcode = 24
else rcode =25
resolve(rcode);
db.close;
})
})
});
}
Approach 2: Deferred Promise
You can refer my blog on promises:
Asynchronous programming with node.js
Your code could be roughly:
// add dependency for q library
var Q = require('q');
exports.signup = function (data) {
const mongodb = require('mongodb').MongoClient;
var rcode;
const url = "mongodb://localhost:27017";
var defered = Q.defer();
mongodb.connect(url,(err,db) => {
if(err) defered.resolve(24) // here you can use defered.reject('error')
var dbo = db.db('project');
dbo.collection('users').insertOne(data, (err, dres) => {
if (err) rcode = 24
else rcode =25
defered.resolve(rcode)
db.close;
})
})
return defered.promise;
}
Approach 3:
You can use async/await from ES6
I've been trying non-stop to work on this query for my datatables front end.
this is my config.js
var config = {
host : 'localhost',
user : 'root',
password : '',
database : 'ef45db'
}
module.exports = config;
this is the function I want to work with async (wait for the query to return the table's columns name)
async function getColumnNames()
{
try{
aColumns = [];
await connection.query('SHOW COLUMNS FROM '+sTable,
function selectCb(err, results, fields){
console.log("entro a getColumnNames");
if(err){
console.log(err);
}
for(var i in results)
{
aColumns.push(results[i]['Field']);
}
connection.end();
});
}catch (e){
console.log(e);
}
}
and this is the controller code to execute that function:
var mysql = require('mysql2');
var config = require('.././database/config');
var connection = mysql.createConnection(config);
var sIndexColumn = '*';
var sTable = 'users';
var aColumns = [];
module.exports = {
getInfo : async function(req,res,next)
{
var request = req.query;
(async () => await getColumnNames());
console.log(aColumns);
}
I'm trying to get the column's name so I can work with datatable's filtering for backend, since node is async this query was getting executed, but the value was undefined (and still is), I've read hundreds of post regarding promises, bluebird and async methods and trying to make this work, the last I've read a lot thats the best and I choosed it because the code seems cleaner. Any ideas whats happening?
For getColumnNames(), you shouldn't use await because connection.query doesn't return promise. It is a callback function.
However, we can make getColumnNames to return promise.
function getColumnNames() {
const aColumns = [];
return new Promise((resolve, reject) => {
connection.query('SHOW COLUMNS FROM ' + sTable,
function selectCb(err, results, fields) {
console.log("entro a getColumnNames");
if (err) {
console.log(err);
reject(err); // if error happens, reject
}
for (var i in results) {
aColumns.push(results[i]['Field']);
}
connection.end();
resolve(aColumns); // resolve with our database columns
});
});
}
and for your controller we can use async await since getColumnNames returns promise as in
module.exports = {
getInfo: async function (req, res, next) {
var request = req.query;
const aColumns = await getColumnNames();
console.log(aColumns);
}
}
Let me know if it works for you.
Newbie to nodejs,trying to execute multiple functions output to html using nodejs,express and mysql as backend.Need to execute 20 functions on single routing call to combine the output of 20 functions and render as json to html.
My app.js function
var express = require('express');
var router = express.Router();
var path = require('path');
var app = express();
var todo = require('./modules/first');
var todo1 = require('./modules/second');
var connection = require('./connection');
connection.init();
app.get('/', function(req,res,next) {
Promise.all([todo.class1.getUsrCnt(),todo.class1.getTotlAmt(),todo.class1.getTotlOrdrCnt(),todo.class1.getTotlCntRcds(),todo.class1.getTotlScsRcds(),todo.class1.getTotlFailRcds(),todo.class1.getTotlAmtRcds()])
.then(function(allData) {
res.addHeader("Access-Control-Allow-Origin", "http://hostname:8183/");
res.json({ message3: allData });
});
res.send(send response to html);
})
app.get('/second', function(req,res,next) {
Promise.all([todo1.class2.getUsr........])
.then(function(allData) {
res.addHeader("Access-Control-Allow-Origin", "http://hostname:8183/");
res.json({ message3: allData });
});
res.send(send response to html);
})
var server = app.listen(8183, function(){
console.log('Server listening on port '+ server.address().port)
});
My todo.js is
var connection = require('../connection');
var data = {},obj={};
var d = new Date();
var month = d.getMonth() + 1;
var year = d.getFullYear();
obj.getUsrCnt = function getUsrCnt(callback) {
connection.acquire(function(err, con) {
con.query(query1, function(err, result) {
con.release();
data.usrs_cnt = result[0].some;
})
});
}
obj.getTotlAmt = function getTotlAmt(callback) {
connection.acquire(function(err, con) {
con.query(query2, function(err, result) {
con.release();
data.total_amt = result[0].some1;
})
});
}
obj.getTotlOrdrCnt = function getTotlOrdrCnt(callback) {
connection.acquire(function(err, con) {
con.query(query3, function(err, result) {
con.release();
data.total_orders = result[0].some2;
})
});
}
.
.
. functions go on
exports.class1 = obj;
Getting undefined in the promise all and unable to render to the html file.
Not sure about the code you wrote, but as I understand you want to call all the functions, get all the results and return back to the user?
so you can use many libraries that waits for several calls for example, promise based:
Promise.all([todo.getUsrCnt('dontcare'), todo.getTotlAmt('dontcate')])
.then(function(allData) {
// All data available here in the order it was called.
});
as for your updated code, you are not returning the data as promises, you assigning it to the local variable.
this is how your methods should look:
obj.getUsrCnt = function getUsrCnt(callback) {
var promise = new Promise(function(resolve, reject) {
connection.acquire(function(err, con) {
if(err) {
return reject(err);
}
con.query(query1, function(err, result) {
con.release();
resolve(result[0].some);
})
});
});
return promise;
}
as you can see here, I am creating a new promise and returning it in the main function.
Inside the new promise I have 2 methods: "resolve", "reject"
one is for the data and one is for errors.
so when you use the promise like this:
returnedPromise.then(function(data) {
//this data is what we got from resolve
}).catch(function(err) {
//this err is what we got from reject
});
you can see that a promise can or resolved or rejected,
do this to all the methods, and then you start seeing data