the complications in javascript node.js? - javascript

I've learned node.js and javascript lately. I loved node.js a lot, but I am working on a project coded in node.js, mongodb, cordova etc. I notice that I needed to use Promise Object in the code a lot.
I create a module in the project to query the db and bring results. In every exported function I need to declare a local function, then use promise, for example:
I have the following local functions in the Module:
var Initialize = function() {
return new Promise(function(resolve, reject) {
try {
MongoClient.connect("db_url_conn", function(err, database) {
if (err) return console.log(err)
db = database;
return resolve(db);
})
} catch (e) {
console.error(e);
}
});
};
then in every exported function in the module I needed to use:
mongoOperation.prototype.getLength = function() {
Initialize(function(db) {
return db;
}).then(function(db) {
getSize(db).then(function(length) {
console.log(length);
});
});
}
The Question is:
Is that normal according to the nature of node.js and JavaScript nature to use promise a lot?
Do I have any other choices to fulfill that?

Since MongoClient.connect() already returns a promise, you can simplify your code:
var Initialize = function() {
return MongoClient.connect("db_url_conn");
};
...
Initialize().then(function(db) { ... });
However, this will create a new client each time you call Initialize, where you should be reusing the client for better performance and to leverage the built-in connection pool:
var client = MongoClient.connect("db_url_conn");
var Initialize = function() { return client };

Related

Express.js retrieving info from external API and rendering to site

I am kind of stuck on how to handle this solution. The frameworks I'm currently working with is node.js and express.js with pug for views/rendering html. What I am trying to do is render to a single page and use values from separate http get requests from different sites. I want express/node to query the external api's and render a view that I will retrieve via ajax call from the client side javascript. The ajax call using jquery from the client side I have figured out. My issue is how to handle multiple external API calls and place the results into an object to render in my pug view. I'm not sure what the best practice would be to execute this properly. Do I create a model? Also, how do I handle the asynchronous http gets? Use promises? I'm kind of new to node and javascript so I'm trying to see what the best conventions to use.
I hope this makes sense.
Thanks!!!!
apiInfoModel.js
var apiCallInfo = {
apiInfo1: APIDATA
apiInfo2: APIDATA2
apiInfo3: APIDATA3
}
Should I have a function that I call that would return the APIDATA?
apiCalls.js
function getApiInfo1() {
return http.get
}
function getApiInfo2() {
return http.get
}
function getApiInfo3() {
return http.get
}
apiInfoController.js
var apiInfo = require('./apiInfoModel')
var apiCalls = require('./apiCalls')
exports.apiInfo = function(req,res,next){
apiInfo.apiInfo1 = apiCalls.getApiInfo1
apiInfo.apiInfo2 = apiCalls.getApiInfo2
apiInfo.apiInfo3 = apiCalls.getApiInfo3
res.render('apiInfo',{apiInfo: apiInfo})
}
To expand on the comment by #Mauricio Noris Freire
apiCalls.js - Add callback parameters
function getApiInfo1(cb) {
return cb(http.get)
}
function getApiInfo2(cb) {
return cb(http.get)
}
function getApiInfo3(cb) {
return cb(http.get)
}
apiInfoController.js - nest the callbacks to have access to all the results
var apiInfo = require('./apiInfoModel')
var apiCalls = require('./apiCalls')
exports.apiInfo = function(req,res,next){
apiCalls.getApiInfo1(function(info1Result) {
apiCalls.getApiInfo2(function(info2Result) {
apiCalls.getApiInfo3(function(info3Result) {
// now you have all 3 results
apiInfoResult = {
apiInfo1: info1Result,
apiInfo2: info2Result,
apiInfo3: info3Result
}
res.render('apiInfo', { apiInfo: apiInfoResult })
});
});
});
}
This nested structure is referred to as the pyramid of doom because it keeps growing with every asynchronous action you need to do. It can be improved by using a utility library like async https://www.npmjs.com/package/async:
async.parallel( [
apiCalls.getApiInfo1,
apiCalls.getApiInfo2,
apiCalls.getApiInfo3
], function(error, apiInfoResult) {
apiInfoResult = {
apiInfo1: info1Result,
apiInfo2: info2Result,
apiInfo3: info3Result
}
res.render('apiInfo', { apiInfo: apiInfoResult })
});
But the currently recommended way is to use Promises. This is a new API introduced in JavaScript to handle this kind of situations. It's available in recent NodeJS versions. It removes the need for callbacks:
apiCalls.js - Return promises instead of using callbacks (the fetch library does this)
require('whatwg-fetch')
function getApiInfo1() {
return fetch()
}
function getApiInfo2() {
return fetch()
}
function getApiInfo3() {
return fetch()
}
apiInfoController.js - Use Promise.all.
const [
apiInfo1,
apiInfo2,
apiInfo3
] = Promise.all([apiCalls.getApiInfo1, apiCalls.getApiInfo2, apiCalls.getApiInfo3]);
apiInfoResult = { apiInfo1, apiInfo2, apiInfo3 }
res.render('apiInfo', { apiInfo: apiInfoResult })

NodeJS mysql2 Bluebird?

Been testing mysql vs mysql2, seems like 2 has made some improvments however it's not an exact drop in replacement. At the same time Q is a good library which seems easier to integrate with however bluebird seems to take less memory and run faster so...
My current mysql-bluebird connector is as follows and allows for straight forward use of query('SELECT email FROM users.users WHERE id=?',id).then(function(res){var email=res[0][0];});
/* global module, require */
var conf=require('./conf.js').conf;
var mysql = require('mysql');
var Promise = require('bluebird');
var using = Promise.using;
Promise.promisifyAll(require('mysql/lib/Connection').prototype);
Promise.promisifyAll(require('mysql/lib/Pool').prototype);
var pool = mysql.createPool(conf.mysql);
var getConnection = function () {
return pool.getConnectionAsync().disposer(function (connection) {
return connection.release();
});
};
var query = function (command) {
return using(getConnection(), function (connection) {
return connection.queryAsync(command);
});
};
function queryWrapper(q,a){
if(a){
return query(mysql.format(q,a));
}
else{
return query(mysql.format(q));
}
}
module.exports = {
query: queryWrapper
};
So far my attempts ad doing this with mysql2 haven't panned out.
Does anyone have any insights on how to convert this?
Thanks, Jegsar
You can use mysql2-promise. It's a simple wrapper, using q, that promisifies mysql2. If you'd rather use Bluebird, you can look at how this wrapper was created and do it yourself.
node-mysql2 now has Promise api built in, and you can choose which promise implementation you want to use
var mysql = require('mysql2/promise');
mysql.createConnection({
Promise: require('bluebird'), // if not set, global Promise is used
user: 'foo',
password: 'bar',
database: 'baz'
})
.then((conn) => conn.query('select 1+1 as test'))
.then(([rows, fields]) => console.log(rows[0]))

How to reuse a mongo connection with promises

How can I change things around in my db connection call so that I can do db.collection():
// Create a Mongo connection
Job.prototype.getDb = function() {
if (!this.db)
this.db = Mongo.connectAsync(this.options.connection);
return this.db;
};
// I want to be able to do this
Job.prototype.test = function() {
return this.db.collection('abc').findAsync()...
};
// Instead of this
Job.prototype.test = function() {
return this.getDb().then(function(db) {
return db.collection('abc').findAsync()...
});
};
My code always calls getDb so the connection does get created so that is not an issue. ex:
this.getDb().then(test.bind(this));
But I actually string many of these calls around so looking for a cleaner approach.
This works - just wondering if there an overall better approach to dealing with this.
Job.prototype.getDb = function(id) {
var self = this;
return new P(function(resolve, reject) {
if (!self.db) {
return Mongo.connectAsync(self.options.connection)
.then(function(c) {
self.db = c;
debug('Got new connection');
resolve(c);
});
}
debug('Got existing connection');
resolve(self.db);
});
};
I suppose this is really just a mongo connection issue, perhaps not just promises. All the Mongo examples I see either just make all their calls inside of the connect callback or use some framework like Express and assign it on start.
I want to be able to do this
return this.db.collection('abc').findAsync()
No, that's impossible when you don't know whether the database is already connected or not. If you might need to connect at first, and that is asynchronous, then this.db must yield a promise, and you'll need to use then.
Notice that with Bluebird you can shorten that code a bit, and avoid that verbose .then() callback by using the .call() method:
Job.prototype.getDb = function() {
if (!this.db)
this.db = Mongo.connectAsync(this.options.connection);
return this.db;
};
Job.prototype.test = function() {
return this.getDb().call('collection', 'abc').call('findAsync');
};

Returning values from Promise(Bluebird) in Node.js

I am learning Node.js and was looking at the Promise module. I am very new at this so please bare with bad code conventions and stuff. I am trying to build a REST Client API using restify. I have attached my Client Code below:
Here is my Client:
// local functions
function loadResult(err, req, res, obj) {
//console.log(err, req, res, obj);
var result = {
'err': err,
'text': obj
};
console.log(result);
return result;
}
function getClient() {
if (client) {
return client;
} else {
client = restify.createJsonClient(options);
return client;
}
};
function RestClient(headers) {
if (headers) {
global_headers.Authorization = headers.Authorization || global_headers.Authorization;
options.headers = global_headers;
}
};
RestClient.prototype.getClient = getClient;
RestClient.prototype.doGET = function doGET(path) {
return new Promise(function(resolve) {
client.get(path, loadResult);
}).then(function(result) {
console.log("before final return:" + result);
return result;
});
};
// exports
module.exports = RestClient;
In the console when I run the following lines:
var Rest = require('./testRest.js');
var restClient = new Rest();
var client = restClient.getClient();
var data = restClient.doGET('/hello/mark');
I can see the result printed just once on the console. But the Data is just what I believe a Promise. Is there a way for me to get the result in data? Also trying to figure out why the result never got printed the second time.
P.S.: I tried using Promise.promisifyAll(require('restify')) but it kept throwing some error saying fn is not a function which force use Promise in this way.
First of all, there is no such error when I do:
var Promise = require("bluebird");
Promise.promisifyAll(require("restify"));
Secondly, restify unfortunately uses some weird getters when exporting the module, as getters can (and do) have side effects, promisifyAll will not visit them, so the one line magic won't work. You need to promisify the classes manually, e.g.
Promise.promisifyAll(require("restify").JsonClient.prototype);
Then you would call:
client.getAsync(path).spread(function(req, res, obj) {
});

Nodejs delay return for "require"

My setup is as follows:
Nodejs Server
server.js requires utils.js
utils.js loads data from mongodb into memory and exports it
server.js uses a variable that utils.js exports
The issue that I am worried about is the fact that the mongodb call is asynchronous. utils.js returns before the mongodb call is finished, meaning that server.js will use an undefined variable when it continues execution after the require.
What is the best to address this issue? The only thing I could think of is wrapping my server.js code in a giant callback and pass that to the function that makes the mongodb call. It seems a bit messy to me, is there a better way to do it?
Code:
server.js
var utils = require("./modules/utils.js");
console.log(utils);
//Do whatever
utils.js
var mods = [];
var db = require("mongojs").connect("localhost", ["modules"]);
db.modules.find({}, function(err, modules){
mods = modules;
});
module.exports = mods;
What you're referring to is called "callback hell". The easiest way to get out of that is to use a Promise library that simplifies it.
I used a node package called bluebird.
var mysql = require("mysql");
var hash = require("password-hash");
var Promise = require("bluebird");
var settings = require("../settings");
Promise.promisifyAll(require("mysql/lib/Connection").prototype);
Promise.promisifyAll(require("mysql/lib/Pool").prototype);
var db_config = {
user:settings.db.user,
password:settings.db.password,
database:settings.db.database
};
var con = mysql.createPool(db_config);
function query(sql) {
return con.getConnectionAsync().then(function(connection) {
return connection.queryAsync(sql)
.spread(function(rows,fields) {
return rows;
}).finally(function() {
connection.release();
});
});
}
This is a very basic database module I wrote that uses bluebird to promisify the database object.
And here's how it's used. It returns a promise! The benefit here is that not only does it return the clutter of callback hell, it makes sure that your code runs asynchronously and the function does not return before things have stopped processing, like in this case, a database query.
function login(user) {
//check for player existance
var query = 'SELECT p.name,p.password,p.id, pd.x, pd.y FROM player p INNER JOIN player_data pd ON p.id = pd.id WHERE p.name='+mysql.escape(user);
return db.select(query).then(function(rows) {
if (!rows.length) return;
return [
rows[0]
];
});
}
Notice how you return a promise, so that you call the then or spread method to get those database values you just queried, not having to worry about if rows will be undefined by the time you want to use it.
As you say, you need to wrap the entire server in a callback. Node.js works this way, it's asynchronous by nature. A server needs to pass through 3 stages: init, serve and deinit. In your case, that database code goes inside the init stage. You could write something like this.
//server.js
var utils = require ("./modules/utils");
var init = function (cb){
//init the utils module, the http server and whatever you need
utils.init (function (error){
if (error) return handleError (error);
cb ();
});
};
var serve = function (){
//Start listening to the http requests
};
var deinit = function (cb){
//This is typically executed when a SIGINT is received, see link1
};
init (serve);
//utils.js
//You could write a wrapper for the db instance, see link2
var mongodb = require ("mongojs");
var db;
module.exports.init = function (cb){
db = mongodb.connect ("localhost", ["modules"]);
db.modules.find ({}, function (err, modules){
if (err) return cb (err);
cb (null, modules);
});
};
I don't recommend using promises, they are slower than raw callbacks. You don't need them at all.
link1 - SIGINT
link2 - db wrapper

Categories

Resources