NodeJS callback - Access to 'res' - node.js

I am using the Express framework and I have the following in one of my route files:
var allUsersFromDynamoDb = function (req, res) {
var dynamodbDoc = new AWS.DynamoDB.DocumentClient();
var params = {
TableName: "users",
ProjectionExpression: "username,loc,age"
};
dynamodbDoc.scan(params, function (err, data) {
if (err) {
console.error("Unable to query. Error:", JSON.stringify(err));
res.statusCode = 500;
res.send("Internal Server Error");
} else {
console.log("DynamoDB Query succeeded.");
res.end(JSON.stringify(data.Items));
}
});
}
I am using the above function in one of my routes:
router.get('/users', allUsersFromDynamoDb);
Now the callback that I am defining while making a call to the "scan" on dynamodbDoc can be pretty useful if defined as a separate function. I can re-use that for some of my other routes as well.
But how can I can still get access to the "res" inside this new function?
I think I should be using "closure" but I can't seem to get it exactly right. I think I would need to maintain the signature of the new callback function to expect 2 params, "err" and "data" as per the following page:
http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#scan-property
Any ideas on how this can be done?

You can use that function as middleware of every routes you want http://expressjs.com/en/guide/using-middleware.html
The new route with the middleware:
var middlewares = require('./middlewares'),
controllers = require('./controllers');
router.get('/users', middlewares.allUsersFromDynamoDb, controllers.theRouteController);
The middleware (middlewares.js) where you pass your data to req so you can use that data everywhere you have req:
exports.allUsersFromDynamoDb = function (req, res, next) {
var dynamodbDoc = new AWS.DynamoDB.DocumentClient();
var params = {
TableName: "users",
ProjectionExpression: "username,loc,age"
};
dynamodbDoc.scan(params, function (err, data) {
if (err) {
console.error("Unable to query. Error:", JSON.stringify(err));
next("Internal Server Error");
} else {
console.log("DynamoDB Query succeeded.");
req.dataScan = JSON.stringify(data.Items);
next();
}
});
};
And finally the controller (controllers.js):
exports.theRouteController = function (req, res) {
// Here is the dataScan you defined in the middleware
res.jsonp(req.dataScan);
};

Based on Michelem's answer here I tried something which makes things a bit cleaner and code more reusable:
var allUsersFromDynamoDb = function (req, res, next) {
var dynamodbDoc = new AWS.DynamoDB.DocumentClient();
var params = {
TableName: "users",
ProjectionExpression: "username,loc,age"
};
dynamodbDoc.scan(params, function (err, data) {
req.err = err;
req.data = data;
next();
});
}
Now I declare another function:
var processUserResults = function (req, res, next) {
if (req.err) {
console.error("Unable to query. Error:", JSON.stringify(req.err));
res.statusCode = 500;
res.send("Internal Server Error");
} else {
console.log("DynamoDB Query succeeded.");
res.end(JSON.stringify(req.data.Items));
}
};
And finally this:
router.get('/users', [allUsersFromDynamoDb, processUserResults]);
All I need to do in the original "function(err, data)" callback is always set 2 values:
req.err = err
req.data = data
And call next(). And processUserResults can similarly be used for other routes.
Still curious to find out if there are any other efficient solutions.

Related

How to pass data from model to router in Node.js

I'm new to Node.js and am trying to pass some data from my DB model back to the router but I'm unable to find a solution. I have the following route file that makes a call to model:
Route file:
var express = require('express');
var router = express.Router();
var db = require('../db');
var customers = require('../models/customers');
db.connect(function(err) {
if (err) {
console.log('Unable to connect to MySQL.')
process.exit(1)
}
});
router.post('/', function(req, res) {
customers.checkPassword(req.body.cust_id, req.body.password);
res.sendStatus(200);
});
Model file:
var db = require('../db.js');
module.exports.checkPassword = function(cust_id, password) {
var sql = "SELECT Password FROM Shop.customers WHERE ID =" + cust_id;
db.get().query(sql, function (err, res, fields) {
result = res[0].Password;
if (err) throw err
});
};
My question is: how could I pass the queried result Password back to my Route file so that I can do this:
console.log('Password is', result);
I appreciate any help on this.
I'd use a promise
Model file
module.exports.checkPassword = function(cust_id, password) {
return new Promise(function(resolve, reject) {
const sql = "SELECT Password FROM Shop.customers WHERE ID =" + cust_id;
db.get().query(sql, function (err, res, fields) {
if (err) return reject(err)
result = res[0].Password;
return resolve(result);
});
});
};
Route file
var express = require('express');
var router = express.Router();
var db = require('../db');
var customers = require('../models/customers');
db.connect(function(err) {
if (err) {
console.log('Unable to connect to MySQL.')
process.exit(1)
}
});
router.post('/', function(req, res) {
customers.checkPassword(req.body.cust_id, req.body.password)
.then((result) => {
// DO: something with result
res.status(200).send();
})
.catch(console.log); // TODO: Handle errors
});
With async/await
router.post('/', async function(req, res) {
try {
const result = await customers.checkPassword(req.body.cust_id, req.body.password)
// DO: something with the result
} catch (e) {
console.log(e); // TODO: handle errors
} finally {
res.status(200).send();
}
});
I assume console.log('Password is', result); is just for test prupose, obviously you should never log a password! Also I suggest to move the callbabck of the routes do a different module, to improve code redability.
You might also find useful promise-module module on npm, basically a promise wrapper around mysql.
You can delegate the credential control to another function in your DB file where you can decide on what kind of data you want to return on success and failure to find such data. Then you can access it from where you are calling it.

How to make reusable function code in Expressjs?

I am newbie in ExpressJs and module pattern in my project. Now, i am stuck that how to use created controller function in another controller. Please look at example :-
menu.ctrl.js
------------
module.exports.save=function(req,res,next){
//here some logic
//somethings like validate req.body,etc
menu.save(function(err){
if(err) return next(err);
res.json({msg:'menu save'})
})
}
user.ctrl.js
------------
var user=require('./user.model')
var menuCtrl=require('./menu.ctrl')
module.exports.save=function(req,res,next){
//here some logic
user.save(function(err){
if(err) return next(err);
//HERE I WANT TO USE `menuCtrl.save()` function
res.json({msg:'success'});
})
}
Decoupling your controller logic from your model logic will allow you reuse logic and make your application easier to maintain.
The idea is that controllers' purpose is to format input and output to and from you application, while models handle actual data manipulation. (This is a typical Rails-like MVC pattern for REST APIs)
To your example:
menuController.js
var menuModel = require('./menuModel');
module.exports.save = function(req, res, next) {
menuModel.save(req.body, function(err) {
if(err) return next(err);
res.json({msg:'menu save'})
});
};
menuModel.js
module.exports.save = function(body, callback) {
// Save menu to the DB
menu.save(body, callback);
};
userController.js
var userModel = require('./userModel');
module.exports.save = function(req, res, next) {
userModel .save(function(err){
if(err) return next(err);
res.json({msg:'success'});
});
}
userModel.js
var menuModel = require('./menuModel');
module.exports.save = function(body, callback) {
// Save user to the DB
user.save(body, function(err, res) {
if (err) return callback(err);
menuModel.save(body, callback);
});
};
Rule of thumb, keep as less business logic as possible in controllers.
//Here is a solution if you are using same route file
//
var getNotificationSetting = async function (user_id) {
let params = {}
params = await NotifcationSetting.findAll({
where: { ns_user_id : user_id },
});
return params;
}
//now calling in action
router.get('/', async function(req, res, next) {
let params = {}
//for setting section
params = await getNotificationSetting(req.session.user.user_id);
});

res is not defined in Node.js (Mongoose)

How can I put res in a normal function i.e not an exported one which is not part of routes?
function createNewStudent(v,callBackOne){
if (callBackOne) {
studentInfo.callBackOneStudent = callBackOne;
}
// common filter json
var filterjson = common.defaultFilterJson();
filterjson['active'] = true;
filterjson['email'] = v.email;
// student initialization
var student = new Student(v);
async.waterfall([
function (done) {
student.save(function (err) {
if (!err) {
studentInfo.callBackOneStudent();
Employee.update({_id: student.created_by},{"$push": { "students": student._id } }).exec(function (err, employee) { });
done();
}
});
}
}
});
},
function (done) {
var url = config.mailer.studentActivateUrl + student._id;
---error is here-----
res.render('modules/users/server/templates/student-confirmation-email', {
name: student.first_name + ' ' + student.last_name,
appName: 'GAIPP',
url: url
}, function (err, emailHTML) {
done(err, emailHTML, student);
});
}
});
My error is 'res' is not defined. Can anyone please help me to solve this error?
The only way that you can put res in a function is if you somehow supply it to that function at runtime. Remember that res is meaningful only in request handling. Outside of the request handler your function couldn't even know which request to respond to because there might be several requests served at the same time.
If you want to have a function that has access to res then you have those options:
Use a nested function in your request handler, e.g.
app.get('/foo', function (req, res) {
function x() {
// you can use res here
}
x();
});
Add res as an argument:
function x(res) {
// you can use res here
}
app.get('/foo', function (req, res) {
x(res);
});
Another option would be to add a callback to your function that would be passed by the handler:
function x(args, cb) {
// you cannot use res here
// but you can call the callback:
cb(null, 'something');
}
app.get('/foo', function (req, res) {
x(function (err, data) {
if (err) {
// handle error
}
// use res here with data supplied by x()
res(data);
});
});
Instead of using callback your x() function could also return a promise.

How do I return my data from a function?

I've written a small database function that gets some data from the DB and puts it into the format that I want, but I'm having difficulties returning the data to display it with Express. The database function is as follows:
function getAllEvents(req, res, next) {
db.any('select * from sensors, events where sensors.sensorid = events.sensorid')
.then(function (data) {
var final = [];
data.forEach(function(datas){
if (!final[datas.sensorid]){
final[datas.sensorid] = {};
}
if (!final[datas.sensorid].name){
final[datas.sensorid].name = datas.name;
final[datas.sensorid].signatures = {};
}
if (!final[datas.sensorid].signatures[datas.signature]){
final[datas.sensorid].signatures[datas.signature] = {};
final[datas.sensorid].signatures[datas.signature].id = "sen" + datas.sensorid + "sig" + datas.signature;
final[datas.sensorid].signatures[datas.signature].signature = datas.signature;
final[datas.sensorid].signatures[datas.signature].message = datas.message;
final[datas.sensorid].signatures[datas.signature].events = {};
}
final[datas.sensorid].signatures[datas.signature].events[datas.eventid] = datas;
})
return final;
})
.catch(function (err) {
console.log("Something went wrong! ", err)
});
}
And the router function to call it is this:
router.get('/events', function(req, res, next) {
db.getAllEvents(function(err, data){
res.render('events', { data: data });
});
});
I think the router function is waiting indefinitely for the data though as I get no errors but the page never loads. What am I doing wrong?
What am I doing wrong?
This code:
router.get('/events', function(req, res, next) {
db.getAllEvents(function(err, data){
res.render('events', { data: data });
});
});
is ok besides the fact that you are not checking for errors. Notice that getAllEvents is expecting a function as an argument.
now Let's look at your getAllEvents function prototype
function getAllEvents(req, res, next) {
It is simply not correct and should have been something like
function getAllEvents(callback) {
Then you would be able to call the callback and "return" the result like this
return callback(null,data);
or if an error occurred during you database connection pass the error to the callback
return callback(err);

Node/Express function and callback are not breaking with return

I am creating a 'refresh data' function in Node and I cannot figure out where to place the callbacks and returns. The function continues to run. Below is a list of things the function should do. Could someone help out?
Check if a user has an api id in the local MongoDB
Call REST api with POST to receive token
Store token results in a MongoDB
Terminate function
./routes/index.js
router.post('/refresh', function(req, res) {
var refresh = require('../api/refresh');
refresh(req, function() { return console.log('Done'); });
});
../api/refresh.js
var callToken = require('./calltoken');
var User = require('../models/user'); // Mongoose Schema
module.exports = function(req, callback) {
User.findOne( {'username':req.body.username}, function(err, user) {
if(err) { console.log(err) }
if (user.api_id == 0) {
callToken.postToken(req.body.username, callback);
} else { // Do something else }
});
};
./calltoken.js
var request = require('request');
var Token = require('../models/token'); // Mongoose Schema
module.exports = {
postToken: function(user, callback) {
var send = {method:'POST', url:'address', formData:{name:user} };
request(send, function(err, res, body) {
if(err) { console.log(err) }
if (res.statusCode == 201) {
var newToken = new Token();
newToken.token = JSON.parse(body).access_token['token'];
newToken.save(function(err) {
if(err) { console.log(err) }
return callback();
});
}
});
}
};
I'm not an expert in Express but everywhere in you code in lines with if(err) { console.log(err) } you should stop execution (maybe of course not - up to you app) and return 400 or 500 to client. So it can be something like
if(err) {
console.log(err);
return callback(err); // NOTICE return here
}
On successful execution you should call return callback(null, result). Notice null as a first argument - it is according nodejs convention (error always goes as first argument).

Resources