How should I create custom modules that require a mongodb connection? - node.js

I'm working on a web app with nodejs, express, and mongodb.
In my 'main' file where I listed for API calls, I include a Users class that has methods like Users.authenticate(userObject, callback), and Users.getById(userId, callback).
Sorry for this long code snippet. It's just a snippet of my users class.
function Users (db) {
if (!db) {
return {'message': 'creating an instance of Users requires a database'}
} else {
this.db = db;
return this;
}
}
Users.prototype.authenticate = function (user, callback) {
if (!user.username) {
return {'message': 'Users.authenticate(user, callback) requires user.username'};
} else if (!user.password) {
return {'message': 'Users.authenticate(user, callback) requires user.password'};
} else if (!callback) {
return {'message': 'Users.authenticate(user, callback) requires callback(err, user)'};
}
this.db.collection('users', function (err, collection) {
if (err) {return {'message': 'could not open users collection'}};
/* query for the user argument */
collection.findOne(user, function (err, doc) {
if (!err) {
if (!doc) {
callback({'message': 'user does not exist'}, null);
} else {
callback(null, doc);
}
} else {
callback({'message': 'error finding user'}, null);
}
});
});
};
exports.Users = Users;
That's it
I pass an open DB connection to my Users class, and make a call like the following:
var server = new mongo.Server('localhost', '27017', {auto_reconnect: true});
var db = new mongo.Db('supportdash', server, {"fsync": true});
// open connection to be, init users
db.open(function (err, db) {
var users = new Users(db);
users.authenticate({"username": "admin", "password": "password"}, function (err, user) {
// do something with the error, or user object
});
});
Now for my questions
Should I be passing an open db connection, or should I be passing the info needed (localhost, port, database name) for the Users class to manage its own connection?
I tried to set up testing with jasmine-node, but I ended up with a lot of problems with async database calls. I wanted to add a user, then test that Users.authenticate was working. I used Jasmines runs() and waitsfor() async helpers, but I could not get it to work. I then ran into an issue that took me a while to debug (with a different class), and testing would have saved me a lot of time. Any advice on how I would test my classes that interact with a mongodb database?

Related

NodeJS class with Promise in method

I am starting with NodeJS, been writing some code for a couple days. Right now I am creating a new Winston Transport, in order to use MSSQL or OracleDB as the log destination.
I decided to create a DB class to manage connections, inserts, etc to the DB.
My idea is to declare a connection (null) in the constructor, and then have methods to create the connection, insert, etc.
However, as connecting is async, and Winston transport calls the log method, I need to create the connection, make the insert and close (or leave open the connection).
So I went with this:
class db {
constructor(){
this.connection = null;
}
connect(){
return new Promise(function(resolve, reject){
if (this.connection == null){
if (config.Logging.DB.type == 'mssql'){
const dbOptions = {
user: config.Logging.DB.user,
password: config.Logging.DB.password,
server: config.Logging.DB.mssql.server,
database: config.Logging.DB.mssql.database,
options: {
encrypt: config.Logging.DB.encrypt
}
};
this.connection = new mssql.ConnectionPool(dbOptions, err => {
if (err) reject('Can\'t establish a DB connection.');
this.connection.connect(err => {
if (err) reject(err);
resolve();
});
});
}
}else{
resolve();
}
});
}
insert(query){
if (config.Logging.DB.type == 'mssql'){
this.connection.request().query(query, err => {
console.log(err);
});
}
}
}
My idea is to create a db class instance upon starting the app, and then use that open connection to make any inserts (each time winston calls the log method), like this:
const db = require('./functions/db');
const db_instance = new db();
//Custom DB transport
class DBTransport extends Transport {
constructor(opts){
super(opts);
//db.getConnection();
}
log(info, callback) {
//write
db_instance.connect().then(()=>{
db_instance.insert(`insert into logs (message) values ("${info.message}")`);
}).catch((err)=>{
console.log(err);
});
callback();
}
}
But this isn't working at all. First of all I get an error as in the db class connect method I am trying to access 'this.connection' inside a the promise and I am not being able to.
And I also want to keep the connection open, as winston may call the log method lots of times (I have a process that will call the log method over 5K times).
I did try using some of the available winston-mssql transports out there... but they are all updated, and not working with the current version of winston.
Can anyone help?
You'll need to save the promise outside of your connect function and return that memoized promise each time like this:
class DBTransport {
connect() {
if (!this._pool) {
// Memoize (cache) the connection so you don't have to remake it every time
this._pool = new Promise(function(resolve, reject) {
const pool = new mssql.ConnectionPool(dbOptions);
return pool.connect().then(function() {
return pool;
});
});
}
return this._pool;
}
insert(query) {
// Always refetch your connection
return this.connect().then(function(pool) {
return pool.request().query(query);
})
}
}

Serverless - AWS Lamda with MongoDB Atlas Queries Not Running

I've created an AWS account and want to use MongoDB Atlas with AWS Lambda.
The only dependency I've downloaded is mongodb locally.
npm install mongodb
Driver based connection string given from mongoDB Atlas for Nodejs is
var uri = "mongodb+srv://kay:myRealPassword#cluster0.mongodb.net/test";
MongoClient.connect(uri, function(err, client) {
const collection = client.db("test").collection("devices");
// perform actions on the collection object
client.close();
});
I think the connection is successful, because err parameter is NULL.
But I cannot figure out how to create collection, how to find results, how to insert documents.
I've tried this code
module.exports.hello = (event, context, callback) => {
var MongoClient = require('mongodb').MongoClient;
var uri = "mongodb+srv://kay:myRealPassword#cluster0.mongodb.net/test";
MongoClient.connect(uri, function(err, client) {
const collection = client.db("test").collection("devices");
collection.insert( { "msg" : "My First Document" } );
var results = client.db("test").collection("devices").find();
console.log(results);
client.close();
callback(null, { message: 'Go Serverless v1.0! Your function executed successfully!', event });
});
};
but it returns (in Windows console) a huge Object in JSON format, its like a configuration data (not a query result)
enter image description here
I'm executing this code locally by
sls invoke local --function hello
The general idea is to check if there is an error in the connection, the insert, and so on. Take at look at this error checking:
if (error) return 1;
There are more sophisticated methods, but for your case this should do the work.
This is a example of how it show look your script:
MongoClient.connect(uri, (error, client) => {
if (error) return 1; // Checking the connection
console.log('Connection Successful');
var db = client.db('mydb'); // Your DB
let newDocument = { "msg" : "My First Document" }; // Your document
db.collection('mycollection').insert(newDocument, (error, results) => { // Your collection
if (error) return 1; // Checking the insert
console.log('Insert Successful');
})
db.collection('mycollection')
.find({})
.toArray((error, accounts) => {
if (error) return 1; // Checking the find
console.log('Find Successful');
console.log(accounts);
return 0;
})
})
And you should have an output like this:
Connection Successful
Insert Successful
Find Successful
[ { _id: 5a857dd2c940040d85cbe5f2, msg: 'My First Document' } ]
If your output is not like this, well the missing log would point the place where you have your error.

node.js Global connection already exists. Call sql.close() first

I'm trying to create web services using node.js from an sql server database,in the frontend when i call those 2 webservices simultaneously it throws an error Global connection already exists. Call sql.close() first .
Any Solution ?
var express = require('express');
var router = express.Router();
var sql = require("mssql");
router.get('/Plant/:server/:user/:password/:database', function(req, res, next) {
user = req.params.user;
password = req.params.password;
server = req.params.server;
database = req.params.database;
// config for your database
var config = {
user: user,
password: password,
server: server,
database:database
};
sql.connect(config, function (err) {
// create Request object
var request = new sql.Request();
// query to the database and get the records
request.query("SELECT distinct PlantName FROM MachineryStateTable"
, function (err, recordset) {
if (err) console.log(err)
else {
for(i=0;i<recordset.recordsets.length;i++) {
res.send(recordset.recordsets[i])
}
}
sql.close();
});
});
});
router.get('/Dep/:server/:user/:password/:database/:plantname', function(req, res, next) {
user = req.params.user;
password = req.params.password;
server = req.params.server;
database = req.params.database;
plantname = req.params.plantname;
// config for your database
var config = {
user: user,
password: password,
server: server,
database:database
};
sql.connect(config, function (err) {
// create Request object
var request = new sql.Request();
// query to the database and get the records
request.query("SELECT distinct DepName FROM MachineryStateTable where PlantName= '"+plantname+"'"
, function (err, recordset) {
if (err) console.log(err)
else {
for(i=0;i<recordset.recordsets.length;i++) {
res.send(recordset.recordsets[i])
}
sql.close();
}
});
});
});
module.exports = router;
You have to create a poolConnection
try this:
new sql.ConnectionPool(config).connect().then(pool => {
return pool.request().query("SELECT * FROM MyTable")
}).then(result => {
let rows = result.recordset
res.setHeader('Access-Control-Allow-Origin', '*')
res.status(200).json(rows);
sql.close();
}).catch(err => {
res.status(500).send({ message: `${err}`})
sql.close();
});
From the documentation, close method should be used on the connection, and not on the required module,
So should be used like
var connection = new sql.Connection({
user: '...',
password: '...',
server: 'localhost',
database: '...'
});
connection.close().
Also couple of suggestions,
1. putting res.send in a loop isn't a good idea, You could reply back the entire recordsets or do operations over it, store the resultant in a variable and send that back.
2. Try using promises, instead of callbacks, it would make the flow neater
You must use ConnectionPool.
Next function returns a recordset with my query results.
async function execute2(query) {
return new Promise((resolve, reject) => {
new sql.ConnectionPool(dbConfig).connect().then(pool => {
return pool.request().query(query)
}).then(result => {
resolve(result.recordset);
sql.close();
}).catch(err => {
reject(err)
sql.close();
});
});
}
Works fine in my code!
if this problem still bother you, then change the core api.
go to node_modules\mssql\lib\base.js
at line 1723, add below code before if condition
globalConnection = null
In case someone comes here trying to find out how to use SQL Server pool connection with parameters:
var executeQuery = function(res,query,parameters){
new sql.ConnectionPool(sqlConfig).connect().then(pool =>{
// create request object
var request = new sql.Request(pool);
// Add parameters
parameters.forEach(function(p) {
request.input(p.name, p.sqltype, p.value);
});
// query to the database
request.query(query,function(err,result){
res.send(result);
sql.close();
});
})
}
Don't read their documentation, I don't think it was written by someone that actually uses the library :) Also don't pay any attention to the names of things, a 'ConnectionPool' doesn't seem to actually be a connection pool of any sort. If you try and create more than one connection from a pool, you will get an error. This is the code that I eventually got working:
const sql = require('mssql');
let pool = new sql.ConnectionPool(config); // some object that lets you connect ONCE
let cnn = await pool.connect(); // create single allowed connection on this 'pool'
let result = await cnn.request().query(query);
console.log('result:', result);
cnn.close(); // close your connection
return result;
This code can be run multiple times in parallel and seems to create multiple connections and correctly close them.

Storing Collection result from mongoDB in Node

I am new to node and I have read the data from mongoDB successfully.
But I would like to store the whole data from the Collection into a variable in nodejs as I would like to use them in the index page.
I do not know how to store it.
// Connection URL
var url = 'mongodb://localhost:27017/test';
// Use connect method to connect to the Server
MongoClient.connect(url, function (err, db) {
assert.equal(null, err);
console.log("Connected correctly to server");
seriescollection = db.collection('series');
});
var findseries = function (db, callback) {
var cursor = db.collection('series').find();
cursor.each(function (err, doc) {
assert.equal(err, null);
if (doc != null) {
console.dir(doc);
} else {
callback();
}
});
};
MongoClient.connect(url, function (err, db) {
assert.equal(null, err);
//insertDocument(db, function () {});
findseries(db, function () {
db.close();
});
});
My sample JSON object in MongoDb is
{
"_id" : "b835225ba18",
"title" : "Name",
"imageurl" :"https://promotions.bellaliant.net/files/Images/TMN/Ballers-June2015.jpg",
"namespaceId" : "UNI890"
}
I would like to access all the fields and create a page based on the fields that I have stored. I need to access all the fields and that is my main goal.
This is a pet project I am working on a leisure time to learn MEAN stack a bit.
Thanks a lot for your help!!!!
There's a few issues with this code, but I think what you're looking for is the toArray method:
var findseries = function (db, callback) {
db.collection('series').find().toArray(function(err, allTheThings) {
// Do whatever with the array
// Spit them all out to console
console.log(allTheThings);
// Get the first one
allTheThings[0];
// Iterate over them
allTheThings.forEach(function(thing) {
// This is a single instance of thing
thing;
});
// Return them
callback(null, allTheThings);
}
}
More here: https://docs.mongodb.org/manual/reference/method/cursor.toArray/
And here: https://mongodb.github.io/node-mongodb-native/api-generated/cursor.html#toarray

What will be the best way to reuse a CouchBase conneciton

I am using the following code for connecting the CouchBase
couchbase.connect(config.CouchBaseConnector, function (err, CouchBaseDB) {
if (err) {
throw (err)
}
CouchBaseDB.set(keyPush, docPush, function (err, meta) {
if (err) { console.log(err); }
});
}
But its creating multiple number of connection.
Can someone help me out to fix the issue. Basically I want to do something like connection pool and keep re-using.
I came across a doc from CouchBase regarding the same. But not able to figure it out how exactly it works and the steps to deploy the same on windows 7 64-bit version.
Update:
I think moxi-server is not released for Windows OS as of now.
The Couchbase Node SDK is a Connection Pool itself. It is responsible of managing connection to the cluster, be alerted about any change in the server topology (add/remove/failed nodes)
This is why most of the time you put your code in a global callback method and reuse the connection
var express = require('express'),
driver = require('couchbase'),
routes = require('./routes');
dbConfiguration = {
"hosts": ["my-couchbase-server:8091"],
"bucket": "bucket"
}
driver.connect(dbConfiguration, function(err, cb) {
if (err) {
throw (err)
}
// put your application logic here
});
If you want to use a global variable you need to wait for the callback and be sure that the connection is established before using it.
I found the following code is working for me.
Please anyone has better solution please post it, I always welcome that.
GLOBAL.CouchBaseDBConnection = undefined;
function OpenCouchBase(callback) {
if (CouchBaseDBConnection == undefined) {
couchbase.connect(config.CouchBaseConnector, function (err, couchbaseOpenCon) {
if (err)
return console.log("Failed to connect to the CouchBase");
else {
CouchBaseDBConnection = couchbaseOpenCon
callback(null, couchbaseOpenCon);
}
});
}
else { callback(null, CouchBaseDBConnection); }
};
module.exports.OpenPoolCouchBase = OpenCouchBase;
You can use generic resource pool module for Node: node-pool
It is generic pool, so you can adapt it for your needs.
EDIT:
Here is example code:
var poolModule = require('generic-pool');
var pool = poolModule.Pool({
name : 'couch',
create : function(callback) {
couchbase.connect(config.CouchBaseConnector, function (err, couchbaseOpenCon) {
if (err)
return console.log("Failed to connect to the CouchBase");
else {
CouchBaseDBConnection = couchbaseOpenCon
callback(null, couchbaseOpenCon);
}
});
},
destroy : function(client) { client.end(); },
max : 10,
// specifies how long a resource can stay idle in pool before being removed
idleTimeoutMillis : 30000,
// if true, logs via console.log - can also be a function
log : true
});
// acquire connection - callback function is called once a resource becomes available
pool.acquire(function(err, client) {
if (err) {
// handle error - this is generally the err from your
// factory.create function
}
else {
console.log("do whatever you want with the client ...");
pool.release(client);
});
}
});

Resources