Requiring mongoose singleton after fetching from database - node.js

I'm trying to build a singleton with mongoose to store my app config in the database, so instead of building a schema and model and exporting the latter with module.exports I'm fetching the config then exporting it, yet all I get is an empty JSON.
Here's my code for the config model:
var mongoose = require('mongoose');
var configSchema = new mongoose.Schema({
ad: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Ad'
},
max_users: Number
});
var configModel = mongoose.model('Config', configSchema);
configModel.findOne()
.populate('ad')
.exec((error, result) => {
if (!error) {
if (result) {
module.exports = result;
} else {
var default_config = new configModel({
ad: null,
max_users: 100
});
default_config.save();
module.exports = default_config;
}
} else {
throw error;
}
});
In the route, I'm only requiring the file and using it in a route
var config = require('../models/config');
router.get('/', function(req, res, next) {
res.json(config);
}
Please note that dynamically requiring the module in the route scope hasn't produced the problem.
Is it because require couldn't recognize exported variable in the asynchronous task ? Is there a proper way to handle this issue ?

require in CommonJS is synchronous. Try defining a file with no specific module.exports and requiring it in another file will give you a {}
The following might work.
var mongoose = require('mongoose');
var configSchema = new mongoose.Schema({
ad: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Ad'
},
max_users: Number
});
var configModel = mongoose.model('Config', configSchema);
module.exports = function (cb) {
configModel.findOne()
.populate('ad')
.exec((error, result) => {
if (!error) {
if (result) {
module.exports = result;
} else {
var default_config = new configModel({
ad: null,
max_users: 100
});
default_config.save();
cb(null, default_config);
module.exports = default_config;
}
} else {
cb(error);
//throw error;
}
});
}
// In your routes
var config = require('../models/config');
router.get('/', function(req, res, next) {
config(function(err, val) {
if (err) {
// send back error
} else {
res.json(val);
}
})
}

Related

Backbone and Express: concatinating (duplicating) routes on res.redirect

I have an action where I need to update MongoDB entry including _id field, which requires deleting old entry and making a new one, here is server side:
exports.update = function(req, res, next){
var outcome = [];
outcome.previousId = req.params.id;
outcome.newId = req.body.name;
var getPreviousRecord = function(callback) {
req.app.db.models.AccountGroup
.findOne({ _id: req.params.id })
.lean()
.exec(function(err, accountGroups) {
if (err) {
return callback(err, null);
}
outcome.accountGroups = accountGroups;
return callback(null, 'done');
});
};
var makeNewRecord = function(callback) {
var permissions = outcome.accountGroups.permissions;
var fieldsToSet = {
_id: outcome.newId.toLowerCase(),
name: outcome.newId,
permissions: permissions
};
req.app.db.models.AccountGroup
.create(fieldsToSet, function(err, record) {
if (err) {
return callback(err, null);
}
outcome.record = record;
return callback(null, 'done');
});
};
var deletePreviousRecord = function() {
req.app.db.models.AccountGroup
.findByIdAndRemove(outcome.previousId)
.exec(function(err) {
if (err) {
return next(err);
}
res.redirect('admin/account-groups/' + outcome.newId + '/');
});
};
var asyncFinally = function(err) {
if (err) {
return next(err);
}
};
require('async').series([getPreviousRecord, makeNewRecord, deletePreviousRecord], asyncFinally);
};
It works fine, but I can't make this work normally on the front-end, it returns me both old route and a new route, for example:
PUT /admin/account-groups/customers22/admin/account-groups/Customers2233/ 404 213.749 ms - 31
where customers22 is old _id and customers2233 is new _id. If I navigate from another page to new entry it gets route normally.
On client side:
(function() {
'use strict';
app = app || {};
app.Details = Backbone.Model.extend({
idAttribute: '_id',
defaults: {
success: false,
errors: [],
errfor: {},
name: ''
},
url: function() {
return '/admin/account-groups/'+ app.mainView.model.id +'/';
},
parse: function(response) {
if (response.accountGroup) {
app.mainView.model.set(response.accountGroup);
delete response.accountGroup;
}
return response;
}
});
app.DetailsView = Backbone.View.extend({
el: '#details',
events: {
'click .btn-update': 'update'
},
template: Handlebars.compile( $('#tmpl-details').html() ),
initialize: function() {
this.model = new app.Details();
this.syncUp();
this.listenTo(app.mainView.model, 'change', this.syncUp);
this.listenTo(this.model, 'sync', this.render);
this.render();
},
syncUp: function() {
this.model.set({
_id: app.mainView.model.id,
name: app.mainView.model.get('name')
});
},
render: function() {
this.$el.html(this.template( this.model.attributes ));
for (var key in this.model.attributes) {
if (this.model.attributes.hasOwnProperty(key)) {
this.$el.find('[name="'+ key +'"]').val(this.model.attributes[key]);
}
}
},
update: function() {
this.model.save({
name: this.$el.find('[name="name"]').val()
});
}
});
app.MainView = Backbone.View.extend({
el: '.page .container',
initialize: function() {
app.mainView = this;
this.model = new app.AccountGroup( JSON.parse( unescape($('#data-record').html()) ) );
// ...
app.detailsView = new app.DetailsView();
}
});
$(document).ready(function() {
app.mainView = new app.MainView();
});
}());
It probably requires to trigger both model.save and model.destroy or prevent URL being used. Any advice on how to do it is appreciated, thank you.
Edit
Just a typo mistake here that is not related to the question, recklessly checking routes, see as cancelled
I believe the problem is here:
res.redirect('admin/account-groups/' + outcome.newId + '/');
That's a relative path so it'll be appended onto the current URL. I suspect you want something like this:
res.redirect('/admin/account-groups/' + outcome.newId + '/');

Mongoose pagination from server side

I am trying to add server side pagination to a NodeJS, Express and MongoDB API. The API use mongoose to handle the database. I am lost in how to customize the response from the Controller.
Model:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const clientSchema = Schema({
code: {
type: String,
required: [true,'Code no puede estar vacio']
},
name: {
type: String,
required: [true,'Name no puede estar vacio']
}
},{
timestamps: true
});
const Client = module.exports = mongoose.model('clients',clientSchema);
Controller for get all clients:
const mongoose = require("mongoose");
const Client = require('../models/client');
const clientController = {};
clientController.index = (limit, callback) => {
Client.find(callback).limit(limit);
};
module.exports = clientController;
Route to get the clients:
app.get('/api/clients', (req, res) => {
Client.index(limit,(err, client) => {
if (err) {
res.status(500).json({
msg: "Error en aplicacion",
err
});
}
res.status(200).json(client);
});
});
How can I customize the result in the controller to something like this:
[
{
"totalRecords":"99999999999",
"offset":"888888",
"page":"4",
"nextPage":"5"
"result":{...}
}
]
I already have a function to calculate the pagination, But I don't know how to add the information about the pagination in the result of the controller.
Before I was adding the pagination data in the route, But I want to handle the pagination logic in the controller.
Or is better handle the pagination in the route?
Thanks in advance
You can create a method in mongoose model called as paginate :
Add this before declaring mongoose model :
clientSchema.methods.paginate = function(pageNo, callback){
var limit = 10;
var skip = pageNo * (limit - 1);
var totalCount;
//count documents
this.count({}, function(err, count)){
if(err){
totalCount = 0;
}
else{
totalCount = count;
}
}
if(totalCount == 0){
return callback('No Document in Database..', null);
}
//get paginated documents
this.find().skip(skip).limit(limit).exec(function(err, docs){
if(err){
return callback('Error Occured', null);
}
else if(!docs){
return callback('Docs Not Found', null);
}
else{
var result = {
"totalRecords" : totalCount,
"page": pageNo,
"nextPage": pageNo + 1,
"result": docs
};
return callback(null, result);
}
});
});
const Client = module.exports = mongoose.model('clients',clientSchema);
Then in controller change :
app.get('/api/clients', (req, res) => {
//You could put page number in request query ro request params
Client.paginate(req.body.pageNo, function(err, response) {
if (err) {
return res.status(500).json({
message : "Error en aplicacion",
error : err
});
}
return res.status(200).json(response);
});
});

Mongoose not returning document from mongo

I am trying to get Mongoose to query my mongo instance for a specific document (using the _id attribute). I currently have the following route:
router.get('/document/', function (req, res) {
var getDocuments = function (callback) {
var options = { server: { socketOptions: { keepAlive: 1000 } } };
var connectionString = 'mongodb://user:password#server:27017/db';
var pID = req.query.id;
pID = pID.trim();
console.log(pID);
var documentArray = [];
// Connected handler
Mongoose.connect(connectionString, function (err) {
var db = Mongoose.connection.useDb('db');
var pCollection = db.collection("collection");
//grab all items from pCollection
pCollection.find({ '_id': pID }, function (error, pDocument) {
if (error) {
console.log(error);
}
if (pDocument) {
// res.send(JSON.stringify(pDocument));
console.log(pDocument);
}
else {
console.log("nothing");
}
});
db.close();
});
};
getDocuments(function () {
});
});
The result returned is not a json document and does not seem to return a usable value. What am I doing wrong? Thanks for any help in advance!
EDIT:
I went back and changed the route to the following:
router.get('/document/', function (req, res) {
var pID = req.query.id;
pID = pID.trim();
console.log(pID);
Document.findById(pID, function (error, document) {
if (error) {
console.log(error);
}
else {
console.log(document);
}
});
});
I also created the following model:
var mongoose = require('mongoose');
var DocumentSchema = require('../schemas/documents');
var Document = mongoose.model('documents', DocumentSchema, 'Documents');
module.exports = Document;
And used the following schema:
var mongoose = require('mongoose');
var DocumentSchema = new mongoose.Schema({
documenttPK: String,
locationID: String,
docName: String,
SerialNumber: String,
documentID: String,
dateEntered: Date,
activeFlag: Boolean
});
module.exports = DocumentSchema;
My app.js makes a single call to a db file:
var mongoose = require('mongoose');
mongoose.connect('mongodb://user:password#server:27017/db');
But the result is still null. Is something wrong with the above code?

I am having issues with exporting in node

I am trying to make an api endpoint for data coming from dynamoDB. I believe that I have everything connected but when I run postman to check the api (api/db) it doesn't recognize the functions from the db.js in the db.js (for routes). I have run a test on api/test and am getting the information back. Here is the code from both files:
1. This scans the database and I'm trying to export it to another file.
var AWS = require('aws-sdk');
var params = {
TableName : "iotbuttonsn",
//KeyConditionExpression: "serialNumber =:serialNumber",
//ExpressionAttributeValues: {
// ":serialNumber":"*"
//},
ScanIndexForward: false,
Limit: 3,
Select: 'ALL_ATTRIBUTES'
};
AWS.config.update({
region: "us-east-1",
endpoint: "https://dynamodb.us-east-1.amazonaws.com"
});
var docClient = new AWS.DynamoDB.DocumentClient();
var getDatabase = (function(){
return {
scanDB: function(){
docClient.scan(params, onScan);
var onScan = function(err, data){
if (err) {
console.log(err.message);
} else {
console.log('scan success');
len = data.Items.length;
for (n=0; n<len; n++) {
clickTypes[n] = data.Items[n].payload.clickType;
serialNums[n] = data.Items[n].serialNumber;
}
}
};
},
clickTypes: [],
serialNums: []
};
})();
module.exports = getDatabase;
2. This is where I'm trying to input but db.scanDB() isn't working:
var router = require('express').Router();
var db = require('../routes/db.js');
router.get('/', function(req, res){
db.scanDB();
buttons =
[
iot_buttonOne = {
serialNum: db.serialNum[0],
clickType: db.clickTypes[0]
},
iot_buttonTwo = {
serialNum: db.serialNum[1],
clickType: db.clickTypes[1]
}
]
.then(
function scanSuccess(data){
res.json(data);
},
function scanError(err){
res.send(500, err.message);
}
);
});
module.exports = router;
Change your db.scan() function to properly return an asynchronous result:
// db.js
module.exports = {
scanDB: function(cb){
docClient.scan(params, function(err, data) {
var clickTypes = [], serialNums = [];
if (err) {
console.log(err.message);
cb(err);
} else {
console.log('scan success');
len = data.Items.length;
for (n=0; n<len; n++) {
clickTypes[n] = data.Items[n].payload.clickType;
serialNums[n] = data.Items[n].serialNumber;
}
cb(null, {clickTypes, serialNums});
}
});
}
};
Then, when you use it:
var db = require('../routes/db.js');
db.scanDB(function(err, data) {
if (!err) {
// data.clickTypes
// data.serialNums
} else {
// process error
}
});
It really does not good to put the scanDB result on the DB object the way you were doing because there was no way for the caller to know when the asynchronous operation was done. So, since you have to provide some notification for the caller when the async operation is done (either via callback or promise), you may as well just pass the results there too.
Also, the .then() handler in your router.get(...) handler does not belong there. I don't know why it's there at all as there are no promises involved in the code you show. Perhaps a cut/paste error when creating the question?
Note, I removed the IIFE from your getDatabase() definition since there was no benefit to it other than a little more complicated code.

how to communicate from angular's controller to other modules in node-webkit?

I got an angularJS' controller used in node-webkit app, how to call db.js ' save_db from angular's controller? code as follow, thanks:
angular.module('clk.controllers', [])
.controller('homeCtrl', ["$scope",function ($scope) {
$scope.post_it = function(tm) {
}
}])
db.js
var db = mongo.db(db_connection)
var users = db.collection('users')
exports.save_db = function (rec, cb) {
users.insert(rec, {
safe: true
}, function (err, records) {
cb(null, '')
});
};
you would simply require your db.js file and call the Node library as per usual e.g.
var db = require('./db.js');
var app = angular.module('clk.controllers', []);
app.controller('homeCtrl', ["$scope", function ($scope) {
$scope.post_it = function(tm) {
db.save_db(tm, function () {
console.log('record added');
});
};
}]);

Resources