How to make reusable function code in Expressjs? - node.js

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);
});

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.

Put and Delete Comment API endpoint - Express MongoDB/Mongoose

I have created an API endpoint that a client can post to and an API endpoint that retrieves the comments.
I am trying to create another API endpoint which allows the client to update an existing comment by specifying the id of the comment they want to change, and a final endpoint to delete. Here is what I have created so far:
var express = require('express');
var router = express.Router();
var Comment = require('../models/comments');
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
/**
* Adds comments to our database */
router.post('/addComment', function(req, res, next) {
// Extract the request body which contains the comments
comment = new Comment(req.body);
comment.save(function (err, savedComment) {
if (err) throw err;
res.json({
"id": savedComment._id
});
});
});
/**
* Returns all comments from our database
*/
router.get('/getComments', function(req, res, next) {
Comment.find({}, function (err, comments) { if (err)
res.send(err); res.json(comments);
}) });
module.exports = router;
Here are examples of PUT and DELETE functions using ES6 syntax. The update function expects a title and content in the posted body. Yours will look different.
module.exports.commentUpdateOne = (req, res) => {
const { id } = req.params;
Comment
.findById(id)
.exec((err, comment) => {
let response = {};
if (err) {
response = responseDueToError(err);
res.status(response.status).json(response.message);
} else if (!comment) {
response = responseDueToNotFound();
res.status(response.status).json(response.message);
} else {
comment.title = req.body.title;
comment.content = req.body.content;
comment
.save((saveErr) => {
if (saveErr) {
response = responseDueToError(saveErr);
} else {
console.log(`Updated commentpost with id ${id}`);
response.status = HttpStatus.NO_CONTENT;
}
res.status(response.status).json(response.message);
});
}
});
};
module.exports.commentDeleteOne = (req, res) => {
const { id } = req.params;
Comment
.findByIdAndRemove(id)
.exec((err, comment) => {
let response = {};
if (err) {
response = responseDueToError(err);
} else if (!comment) {
response = responseDueToNotFound();
} else {
console.log('Deleted comment with id', id);
response.status = HttpStatus.NO_CONTENT;
}
res.status(response.status).json(response.message);
});
};
Usually when you update with PUT in a REST API, you have to provide the entire document's content, even the fields you are not changing. If you leave out a field it will be removed from the document. In this particular example the update function expects both title and content, so you have to provide both. You can write the update logic however you want though.
The router has functions for put and delete. So it will look something like this:
router
.get('comment/:id', getFunction)
.put('/comment/:id', putFunction)
.delete('/comment/:id', deleteFunction);

NodeJS callback - Access to 'res'

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.

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).

Mongoose model.save() not persisting to database on update

I am developing a mongoose / node.js / express app.
in my routes I am using express' app.param() method to get my model instance into the request - instead of fetching it in each controller action.
i got show and create actions working in my controller - however I am stuck implementing the update action.
here is my relevant controller code:
// mymodel-controller.js
var mongoose = require('mongoose')
var MyModel = mongoose.model('MyModel');
var utils = require('../../lib/utils');
"use strict";
exports.update = function (req, res, next) {
var mymodel = req.mymodel;
mymodel.set(req.body);
mymodel.slug = utils.convertToSlug(req.body.title);
mymodel.save(function(err, doc) {
if (!err) {
console.log('update successful');
// here i see the correctly updated model in the console, however the db is not updated
console.dir(doc);
return res.redirect('/mymodels/' + mymodel.slug);
} else {
console.error('update error', err);
return res.render('mymodels/edit', {
title: 'Edit Model',
model: mymodel,
errors: err.errors
});
}
});
}
The strange thing is, the mongoose save goes through successfully, I don't get any error.
I see 'update successful' and the correctly updated model printed on the console, but not persisted in the database.
I tried also fetching the model manually before updating and saving it, instead of using app.param(..) but I had the same effect.
Any idea what I am missing here?
update
this is the code where I set the req.mymodel - part of a longer routes file.
In my show actions e.g. I also use req.mymodel to display it and it works fine so far.
/**
* mymodel ROUTES
*/
app.param('mymodel', function(req, res, next, id){
MyModel.findOne({ 'slug': id }, function(err, mymodel) {
if (err) {
next(err);
} else if (mymodel) {
req.mymodel = mymodel;
next();
} else {
res.status(404).render('404', {title: "Not found", errorMessage: "The requested mymodel was not found."});
}
});
});
app.get('/mymodels', mymodelsController.index);
app.get('/mymodels/new', mymodelsController.new);
app.post('/mymodels', mymodelsController.create);
app.get('/mymodels/:mymodel', mymodelsController.show);
app.get('/mymodels/:mymodel/edit', mymodelsController.edit);
app.put('/mymodels/:mymodel', mymodelsController.update); // that's the one not working with the code above
app.del('/mymodels/:mymodel', mymodelsController.destroy);
update2
This code, on the other hand, works in my controller and updates the database (I'd much prefer using instance.save() though)
exports.update = function (req, res, next) {
var mymodel = req.param('mymodel');
var query = { slug: mymodel };
var update = {};
update.title = req.param('title');
update.body = req.param('body');
update.points = req.param('points');
update.location = req.param('location');
update.slug = utils.convertToSlug(req.param('title'));
MyModel.update(query, update, function (err, numAffected) {
if (err) return next(err);
if (0 === numAffected) {
return next(new Error('no model to modify'), null);
}
res.redirect('/mymodels/' + update.slug);
});
}
The problem was indeed the way I set the req.param value in my routes.
Instead of doing this in my routes file:
app.param('mymodel', function(req, res, next, id){
MyModel.findOne({ 'slug': id }, function(err, mymodel) {
if (err) {
next(err);
} else if (mymodel) {
req.mymodel = mymodel;
next();
} else {
res.status(404).render('404', {title: "Not found", errorMessage: "The requested mymodel was not found."});
}
});
});
I had to declare a controller action that loads the instance each time, like
app.param('mymodel', mymodelController.load);
which pretty much looks like in this example.

Resources