Node.js module.exports parent/child variable reference - node.js

In node.js I have this scenario:
main.js
module.exports = {
dbHandler: {}
}
const DB_CONNECT = require('dbConnect.js');
const CHILD_MODULE = require('childModule.js');
module.exports.dbHandler = DB_CONNECT.connectDB(); // establishes the connection to the sqlite3 db
// ... give some time to module.exports.dbHandler to be loaded. (lab testing)
CHILD_MODULE.queryDB(); // <----- error occurs
childModule.js
var db = module.parent.exports.dbHandler;
//issue is here. Even after the parent have set dbHandler, this still empty {}.
module.exports.queryDB = function(){
db.all('SELECT * from mytable', (err, rows) => { // callback
console.log(rows);
}
Since DB_CONNECT.connectDB() is async, I give it a while (lab test) to load the database and updating module.exports.dbHandler before calling CHILD_MODULE.queryDB()
the error occurs when db.all is called.
TypeError: db.all is not a function
db still an empty object {}.
What is wrong in this code? How do I make the child's db to access the parent's module.exports.dbHandler ?

First of all, I will not fix your problem directly. I will try to explain my comment in above.
I have had a similar scenario in one of my projects. But I have used MongoDB. My db model looks like this:
var MongoClient = require('mongodb').MongoClient
var url = process.env.MONGO_URI
var collection = 'shortlinks'
var state = {
db: null
}
exports.connect = function (done) {
if (state.db) return done()
MongoClient.connect(url, function (err, db) {
if (err) return done(err)
state.db = db
done()
})
}
exports.get = function () {
return state.db
}
...
and some other methods
And I have accessed this module from different places for the same database connection with this line:
var db = require('../models/db')
I can access the same db instance with getter method and other methods as well.

Related

how to redis cache set and get properly used in nodejs?

I tried node.js crud operation using mongodb and also stored in redis cache. First time I tried to run get method to get data from db and second time I run the get method. It got data from cache but I tried to delete the data in the table and another time to run get method it is not showing data.It is showing empty data. But data is stored in redis cache. how can I solve this issue?
cache.js
// var asyncRedis = require("async-redis")
// var myCache = asyncRedis.createClient()
var redis = require('redis');
const client = redis.createClient()
client.on('connect', function () {
console.log('Redis client connected');
});
client.on('error', function (err) {
console.log('Something went wrong ' + err);
});
var value;
var todayEnd = new Date().setHours(23, 59, 59, 999);
function Get_Value()
{
client.get('products', function(err,results) {
value = JSON.parse(results);
})
return value
}
function Set_Value(products)
{
client.set('products', JSON.stringify(products))
client.expireat('products', parseInt(todayEnd/1000));
}
exports.get_value = Get_Value;
exports.set_value = Set_Value;
routes.py
data = cache.get_value()
console.log(data)
if (data) {
console.log("GET")
res.send(data)
}
else {
console.log("SET")
const r = await db.collection('Ecommerce').find().toArray();
res.send(r)
data = cache.set_value(r)
}
hari,
Your Get_Value looks a little odd to me. The Redis get will be executed asynchronously. So when you place the return value statement outside the callback, it will return right away, value is still undefined.
The easiest way to solve this would be to call Get_Value with a callback to continue when redis GET returns.
function Get_Value(callback) {
client.get('products', function(err,results) {
let value = JSON.parse(results);
>> callback(value);
});
}
You would use it like that:
Get_Value(function(value) {
console.log("products: " + value);
}
An other option would be to use the Promise API from Node Redis (see docs here: https://github.com/NodeRedis/node_redis)
Does this help?

NodeJS + MongoJS: Nested Callbacks Issue

I'm still a n00b with NodeJS so this question may be somewhat elementary.
I'm using MongoJS to read two logically related collections. The first find() returns a value that I pass to the second find() to get the information I need.
I've tried several strategies, the last one (snippet #1) being a class that I export.
Before that I just had a function that did a return, returning the desired value, i.e., "config[0]".
In this code all I did was set the "sapConfig" attribute to the word "test", but when I execute this code the value of "sapConfig" is always "null" after I call the "get_config()" method and - strangest of all - the reference to "this.sapConfig = 'test'" generates an error, i.e., "Cannot set property 'sapConfig' of undefined".
When I had the code just as a simple function with a return statement (snippet #2), no errors were generated but the value returned is always "undefined" although the console.log() statements show that the value of the variable being returned has the desired value. What gives?
Code Snippet #1: Returns Object
"use strict";
var mongojs = require('mongojs'); // MongoDB API wrapper
module.exports = function(regKey) {
this.regKey = regKey;
this.sapConfig = null;
this.get_config = function() {
// Read SAP connection information from our MONGO db
var db = mongojs('mongodb://localhost/MIM', ['Configurations','Registrations']);
db.Registrations.find({ key: this.regKey }, function(err1, registration){
console.log('Reg.find()');
console.log(registration[0]);
db.Configurations.find({ type: registration[0].type }, function(err2, config){
console.log('Config.find()');
console.log('config=' + config[0].user);
this.sapConfig = 'test';
});
});
}
this.get_result = function() {
return this.sapConfig;
}
}
Again, the code in snippet #1, when I make a call to "get_config()", results in an error when it executes the line "this.sapConfig = 'test'".
However, after this error I can execute "obj.get_result()" and I get the value to which it was initialized, i.e., null. In other words, that same code doesn't generate an error saying that the "this" is undefined as .in the "get_config()" method
Code Snippet #2: Using the "return" statement
"use strict";
var mongojs = require('mongojs'); // MongoDB API wrapper
module.exports = function(regKey) {
// Read SAP connection information from our MONGO db
var db = mongojs('mongodb://localhost/MIM', ['Configurations','Registrations']);
db.Registrations.find({ key: regKey }, function(err1, registration){
console.log('Reg.find()');
console.log(registration[0]);
db.Configurations.find({ type: registration[0].type }, function(err2, config){
console.log('Config.find()');
console.log('config=' + config[0].user);
return config[0].user;
});
});
}
When I receive the return value and inspect it, it's "undefined". For example, at the Node CL I issue the following commands:
var config = require('./config') // The name of the module above
> var k = config('2eac44bc-232d-4667-bd24-18e71879f18c')
undefined <-- this is from MongoJS; it's fine
> Reg.find() <-- debug statement in my function
{ _id: 589e2bf64b0e89f233da8fbb,
key: '2eac44bc-232d-4667-bd24-18e71879f18c',
type: 'TEST' }
Config.find()
config=MST0025
> k <-- this should have the value of "config[0]"
undefined
You can see that the queries were successful but the value of "k" is "undefined". What's going on here?
I don't care which approach I use I just need one of them to work.
Thanks in advance!
this.sapConfig is not accessible. Thats because this refers to within the current function. What I like todo, is have a variable that refers to the function instance that you know sapConfig is located in.
Ex:
function Foo() {
var self = this;
this.test = "I am test";
var bar = function(){
return function(){
console.log(this.test); //outputs undefined (because this refers to the current function scope)
console.log(self.test); //outputs "I am test";
}
}
}
Here is your first code snippit with my example implemented:
"use strict";
var mongojs = require('mongojs'); // MongoDB API wrapper
module.exports = function(regKey) {
var self = this;
this.regKey = regKey;
this.sapConfig = null;
this.get_config = function() {
// Read SAP connection information from our MONGO db
var db = mongojs('mongodb://localhost/MIM', ['Configurations', 'Registrations']);
db.Registrations.find({ key: this.regKey }, function(err1, registration) {
console.log('Reg.find()');
console.log(registration[0]);
db.Configurations.find({ type: registration[0].type }, function(err2, config) {
console.log('Config.find()');
console.log('config=' + config[0].user);
self.sapConfig = 'test';
});
});
}
this.get_result = function() {
return self.sapConfig;
}
}
For your second snippet. You are trying to return a value from within your nested callback. Since nested functions are asyncronous, you cannot do that.
Here is how I like to return values from nested callbacks:
Ex2:
//Function example
var functionWithNested = function(done) {
//Notice the done param.
// It is going to be a function that takes the finished data once all our nested functions are done.
function() {
//Do things
function() {
//do more things
done("resultHere"); //finished. pass back the result.
}();//end of 2nd nested function
}(); //end of 1st nested function
};
//Calling the function
functionWithNested(function(result) {
//Callback
console.log(result); //resultHere
})
Here is your code using that example:
"use strict";
var mongojs = require('mongojs'); // MongoDB API wrapper
module.exports = function(regKey, done) {
// Read SAP connection information from our MONGO db
var db = mongojs('mongodb://localhost/MIM', ['Configurations', 'Registrations']);
db.Registrations.find({ key: regKey }, function(err1, registration) {
console.log('Reg.find()');
console.log(registration[0]);
db.Configurations.find({ type: registration[0].type }, function(err2, config) {
console.log('Config.find()');
console.log('config=' + config[0].user);
done(config[0].user);
});
});
}
//Then wherever you call the above function use this format
// if config is the name of the function export above...
new Config().(regKey, function(result){
console.log(result); //config[0].user value
})
Lots and lots of code, but I hope you were able to follow it. Let me know if you have any more questions! Cheers.

Connect synchronously to mongodb

I would like to connect to mongodb first, then run everything else in my application.
To do it I have to write something like:
MongoClient.connect("mongodb://localhost/test", function(err, connection) {
if (err) { console.error(err); }
db = connection;
var app = express();
// Include API V1
require("./apiv1.js")(app, db);
app.listen(3000, function(err) {
if (err) { console.error(err); } else { console.log("Started on *:3000"); }
});
});
This makes my app to be completely indented inside the .connect function... Which looks ugly and takes space while I work on my project.
I think the best solution would be have the MongoDB connection synchronous (even because witout the DB connection my app cannot work so why should I do something while it's connecting?) and then run the rest of my code.
How can I do?
You can't connect to MongoDB synchronously, but you may get rid of this ugly callback from your code.
The best way to do it is to adopt some wrapper around node-mongodb-native driver.
Take a look at the following modules.
mongojs
var mongojs = require('mongojs');
var db = mongojs('localhost/test');
var mycollection = db.collection('mycollection');
mongoskin
var mongo = require('mongoskin');
var db = mongo.db("mongodb://localhost:27017/test", {native_parser:true});
monk
var monk = require('monk');
var db = monk('localhost/test');
var users = db.get('users')
Of course, internally all of them are establishing MongoDB connection asynchronously.
Using the async library, you can aleve some of these issues.
For example in my server startup I do the following :
async.series([
function(callback){
// Initialize the mongodb connection and callback on completion in init.
db.init(function(){
callback();
});
},
function(callback){
// Listen on requests etc.
webServer.init(function(){
callback();
});
},
function(callback){
// Set up anything else that I need
callback();
}
]);
If you are using Node 6 and up versions, you can do something like this:
const MongoClient = require('mongodb').MongoClient;
const url = 'mongodb://localhost:27017/mydb';
let db = null;
getdb();
//your code
async function getdb() {
db = await MongoClient.connect(url);
}
Bring the mongodb library.
Declare the url constant .
Declare the variable db as null.
Call the getdb function.
Create the getdb function which has firt the async word
Assign to the db variable the result of the connection with the key word await.
You can do it with thunky, thunky executes an async function once and caches it, the subsequent calls are returned from the cache.
const MongoClient = require('mongodb').MongoClient;
const thunky = require('thunky');
var connect = thunky(function(cb){
let url = 'mongodb://localhost:27017/test';
MongoClient.connect(url, function(err, client){
console.log('connecting')
cb(err, client);
})
})
connect( (err, client) => {
console.log('connection 1')
})
connect( (err, client) => {
console.log('connection 2')
})
connect( (err, client) => {
console.log('connection 3')
console.log('closing')
client.close();
})
*Note: I am using latest 3.x mongodb driver

Making Mocha test work for my db module

I am new to Mocha, and only a little experience with Node/Express. My DbProvider module works perfectly (mongodb) when I am access it through my Express app. And now I want to test it. I have read the Mocha site and some tutorials I could find. But I have big trouble of finding an real-world example out there that I could follow (any links much appreciated!).
Here is my unsuccessful attempt to write a testfile:
var DbProvider = require('../db').DbProvider;
var assert = require('assert');
var dbProvider = new DbProvider('localhost', 27017, 'mydb');
var util = require('util');
console.log(util.inspect(dbProvider));
describe('DbProvider', function(){
describe('findAllNotes', function(){
it('should return some notes', function(){
dbProvider.findAllNotes({}, function (err, result){
assert(result.length > 0);
});
})
})
})
The output I get is this:
$ mocha
{}
✖ 1 of 1 test failed:
1) DbProvider findAllNotes should return some notes:
TypeError: Cannot call method 'collection' of undefined
at DbProvider.doOperation (/Users/frode/Node/json/db.js:46:11)
at DbProvider.findAllNotes (/Users/frode/Node/json/db.js:56:8)
at Context.<anonymous> (/Users/frode/Node/json/test/test.js:15:18)
(cutting out the rest)
It seems that I am unsuccessful to create the dbProvider. This works perfectly in my app... How can I make this work? (And perhaps also: Is the way I have set it up in general ok?)
Edit: Here is the db.js file:
// Database related
'use strict';
var MongoClient = require('mongodb').MongoClient;
var BSON = require('mongodb').BSONPure;
var ObjectID = require('mongodb').ObjectID;
var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$");
var Validator = require('validator').Validator
var fieldMaxLength = 1024;
//var util = require('util');
var DbProvider = function(host, port, database) {
var dbUrl = "mongodb://"+host+":"+port+"/"+database;
var self = this;
MongoClient.connect(dbUrl, function(err, db) {
self.db = db;
});
};
// Do some basic validation on the data we get from the client/user
var validateParams = function(params, callback) {
// Let´ do a quick general sanity check on the length on all fields
for(var key in params) {
if(params[key].length > fieldMaxLength) callback(new Error('Field ' + key + ' is too long.'));
}
// and the let us check some specific fields better
if (params._id) {
if(checkForHexRegExp.test(params._id)) {
// In case of '_id' we also need to convert it to BSON so that mongodb can use it.
params._id = new BSON.ObjectID(params._id);
} else {
var err = {error: 'Wrong ID format'};
}
}
if(err) callback(err);
}
// Generalized function to operations on the database
// Todo: Generalize even more when user authenication is implemented
DbProvider.prototype.doOperation = function(collection, operation, params, callback) {
validateParams(params, callback);
var operationCallback = function(err, result) {
callback(err, result);
};
this.db.collection(collection, function(err, collection) {
if(operation==='find') {
collection.find().toArray(operationCallback);
} else {
collection[operation](params, operationCallback);
}
});
}
DbProvider.prototype.findAllNotes = function(params, callback) {
this.doOperation('notes', 'find', params, callback);
};
DbProvider.prototype.findNoteById = function(params, callback) {
this.doOperation('notes', 'findOne', params, callback);
};
DbProvider.prototype.saveNote = function(params, callback) {
params.created_at = new Date();
this.doOperation('notes', 'save', params, callback);
};
DbProvider.prototype.deleteNote = function(params, callback) {
this.doOperation('notes', 'remove', params, callback);
};
DbProvider.prototype.findUser = function(params, callback) {
this.doOperation('users', 'findOne', params, callback);
};
exports.DbProvider = DbProvider;
SOLUTION:
After Benjamin told me to handle the async nature of mongodb connecting to the database, and inspired by his suggestion on how to adapt the code, I split the constructor function DbProvider into two parts. The first part, the constructor DbProvider now just saves the db-parameters into a variable. The second part, a new function, DbProvider.connect does the actual async connection. See below.
var DbProvider = function(host, port, database) {
this.dbUrl = "mongodb://"+host+":"+port+"/"+database;
};
DbProvider.prototype.connect = function(callback) {
var self = this;
MongoClient.connect(this.dbUrl, function(err, db) {
self.db = db;
callback();
});
};
So I can now make a Mocha test like this (and async tests also need the "Done" included, like you see in the code below):
var assert = require('assert');
var DbProvider = require('../db').DbProvider;
var dbProvider = new DbProvider('localhost', 27017, 'nki');
describe('DbProvider', function(){
describe('findAllNotes', function(){
it('should return some notes', function(done){
dbProvider.connect(function(){
dbProvider.findAllNotes({}, function (err, result){
assert(result.length > 0);
done();
});
});
})
})
})
Note that the acutal test ("should return some notes") is nothing to be proud of. What I wanted here was to get set up so I am able to test something. Now that I finally acutally can do that, I need to write good tests (something in the the line of having a test database, clear it, test insert a document, test search for a document, and so on...).
And in my Express app, I used to set up the database like this:
var DbProvider = require('./db').DbProvider;
// Setup db instance
var dbProvider = new DbProvider(
process.env.mongo_host || 'localhost',
process.env.mongo_port || 27017,
process.env.mongo_db || 'nki'
);
Now I do the same, but in addition, I call the new connect-function:
// Connect to db. I use (for now) 1 connection for the lifetime of this app.
// And I do not use a callback when connecting here (we do in the testing)
dbProvider.connect(function(){});
Benjamin actually pointed out that it may be ok but not the best practice to have the database set up like this in an Express app. But until I figure out what the best practice really is, I will leave this code as it is. Here is a couple of links reagarding the subject I found (but I have still not concluded of how I will solve it myself):
What's the best practice for MongoDB connections on Node.js? and
[node-mongodb-native] MongoDB Best practices for beginner
If you like, you are very welcome to follow/fork/whatever this project on github. My goal is to get it as production ready I can. The link is
https://github.com/frodefi/node-mongodb-json-server
MongoClient.connect is asynchronous.
From the docs:
callback (function) – this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the initialized db object or null if an error occured.
That means DbProvider.db isn't set yet in the test which is why you're getting undefined.
In here:
MongoClient.connect(dbUrl, function(err, db) {
self.db = db;
});
You're telling it "update self.db after the connection happened", which is at least one event loop tick after this one (but may be more). In your mocha code you're executing your .describe and .it methods right after creating your DbProvider instance which means it was not initialized yet.
I suggest that you re-factor DbProvider to return a callback instead of being a constructor function. Maybe something along the lines of:
var getDbProvider = function(host, port, database,callback) {
var dbUrl = "mongodb://"+host+":"+port+"/"+database;
MongoClient.connect(dbUrl, function(err, db) {
self.db = db;
callback(db);
});
};
Which also means moving all the DBProvider methods to an object (maybe the callback will return a dbprovider object and not just a db?).
Another bug solved by using Unit Tests :)
This is what I used: https://github.com/arunoda/mocha-mongo
It has set of testing helpers for mongodb

sharing mongodb connection object globally in a node js app

how do i share the db object returned from when i call db.open or db.connect across the entire app?
i have a dbconnect.js module as follows :
var mongodb = require('mongodb');
var global_db = '';
// Define options. Note poolSize.
var serverOptions = {
'auto_reconnect': true,
'poolSize': 5
};
// Now create the server, passing our options.
var serv = new mongodb.Server('localhost', 27017, serverOptions);
// At this point, there is no connection made to the server.
// Create a handle to the Mongo database called 'myDB'.
var dbManager = new mongodb.Db('myDB', serv);
// NOW we initialize ALL 5 connections:
dbManager.open(function (error, db) {
// Do something with the connection.
global_db = db;
// Make sure to call db.close() when ALL connections need
// to be shut down.
db.close();
});
function getConnection()
{
return global_db;
}
exports.getConnection = getConnection;
and i am using this dbconnect.js in my app.js as:
var http = require('http');
var db = require('./dbconnect').getConnection();
var collection = db.collection('testcollection');
console.log(db);
console.log(collection);
var server = http.createServer();
server.on('request',route);
server.listen(8000,'127.0.0.1');
console.log('Server running at http://127.0.0.1:8000');
function route(request,response)
{
var url = request.url;
var doc = {};
doc[url] = 'ok';
collection.insert(doc,{w:1},function(err,result)
{
if(err) console.log(err);
else console.log(result);
});
}
in the console, the db and collection variable show empty values, i also tried removing the db.close() call in dbconnect.js but to no use, however the insertion works when i place it inside dbconnect.js file in the dbManager.open function, how do i do this?or any similar alternatives?
You can't do that, because dbManager.open( is async method, but you trying to get data from module synchronously.
Try this:
In dbconnect.js
var on_db_ready = null;
module.exports = {
db_ready:function(db_ready_callback){
on_db_ready = db_ready_callback;
//here we call callback if already have db
if (global_db) on_db_ready(global_db);
},
getConnection:getConnection
};
dbManager.open(function (error, db) {
if (on_db_ready) on_db_ready(db);
global_db= db;
})
in app.js:
var db = require('./dbconnect').db_ready(function(db){
//Here i have my database
//or can use getConnection method
});
this is not very beautiful way, but, I hope, explain your mistake

Resources