Can't update the mongodb document on the fly - node.js

I am quite new to Node.js and MongoDB. I am trying to have this code in which I have a boolean document called test which is initially "false" in Mongodb and I want to alert that when a page /hello is loaded. then go to another page submit a form and update test to true and load /hello again. so this time it should alert true (when I check the database it has been updated to true) but it doesn't alert anything when I test it. Would be great if you let me know when am doing wrong!
here are the relevant codes in my app.js
app.post("/Submitted", function(req,res){
var conditions = mongoose.model('users').findOne({username: req.session.user.username}, function (err, doc){
doc.test = true;
doc.save();
res.redirect('hello');
});
app.get('/hello', function (req, res) {
var umbrella = req.session.user.test;
if (req.session.user) {
res.render('5points', {test: test});
} else {
res.redirect('/');
}
});
and here is my jade file:
script.
function check() {
var test= !{JSON.stringify(test)};
alert(test);
body(onload= "check()")

The res.redirect('hello') is getting triggered before the doc.save gets completed.Remember node.js is non blocking I/O.So you need to call the res.redirect inside the callback of save.
app.post("/Submitted", function(req, res) {
var conditions = mongoose.model('users').findOne({
username: req.session.user.username
}, function(err, doc) {
doc.test = true;
doc.save(function(err) {
if (err) return res.json(message: "error");
res.redirect('hello');
});
}
});
});

Related

How To Bind Node-js DB Query to Web Form

I'm using node and postgres, I'm new to writing async function, what I'm trying to do is a very simple query that will do a total count of records in the database, add one to it and return the result. The result will be visible before the DOM is generated. I don't know how to do this, since async function doesn't return value to callers (also probably I still have the synchronous mindset). Here's the function:
function generateRTA(callback){
var current_year = new Date().getFullYear();
const qry = `SELECT COUNT(date_part('year', updated_on))
FROM recruitment_process
WHERE date_part('year', updated_on) = $1;`
const value = [current_year]
pool.query(qry, value, (err, res) => {
if (err) {
console.log(err.stack)
} else {
var count = parseInt(res.rows[0].count) + 1
var rta_no = String(current_year) + '-' + count
callback(null, rta_no)
}
})
}
For the front-end I'm using pug with simple HTML form.
const rta_no = generateRTA(function (err, res){
if(err){
console.log(err)
}
else{
console.log(res)
}
})
app.get('/new_application', function(req, res){
res.render('new_application', {rta_number: rta_no})
});
I can see the rta_no in console.log but how do I pass it back to the DOM when the value is ready?
Based on the ajax call async response, it will update the div id "div1" when it gets the response from the Node js .
app.js
app.get("/webform", (req, res) => {
res.render("webform", {
title: "Render Web Form"
});
});
app.get("/new_application", (req, res) => {
// Connect to database.
var connection = getMySQLConnection();
connection.connect();
// Do the query to get data.
connection.query('SELECT count(1) as cnt FROM test ', function(err, rows, fields) {
var person;
if (err) {
res.status(500).json({"status_code": 500,"status_message": "internal server error"});
} else {
// Check if the result is found or not
if(rows.length==1) {
res.status(200).json({"count": rows[0].cnt});
} else {
// render not found page
res.status(404).json({"status_code":404, "status_message": "Not found"});
}
}
});
// Close connection
connection.end();
});
webform.pug - Via asynchronous call
html
head
script(src='https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js')
script.
$(document).ready(function(){
$.ajax({url: "/new_application", success: function(result){
$("#div1").html(result.count);
}});
});
body
div
Total count goes here :
#div1
value loading ...
That seems okay, I'm just not sure of this:
The result will be visible before the DOM is generated
This constraint defeats the purpose of async, as your DOM should wait for the returned value to be there. Instead of waiting for it you could just render the page and once the function returns and runs your callback update the value.
Also, perhaps it's worth having a look into promises

Wait for validation (serverside) to complete befor insert into database

I am pretty new to Node.js or Javascript in general when it comes to serverside stuff. Currently I am tring to validate some of the user input and set default values if something is wrong. Now if I run my validation the json object appears in the database befor my validation is completed.
The way I am doing the validation isnt maybe the best right now but if someone can explain me the behavior, I am pretty sure i can understand Javascript alot better in the future.
Is there a better way of doing validation (without mongoose or other ODM modules) with callbacks, middleware or should I use some async module?
Here is my code:
module.exports = function(app, express, todoDB, listDB, statusDB) {
var moment = require('moment');
var todoRouter = express.Router();
todoRouter.post('/', function(req, res, next) {
console.log('1');
if (!(moment(req.body.createDate).isValid())) {
req.body.createDate = moment().format("DD-MM-YYYY HH:mm:ss");
}
else {
req.body.createDate = moment(req.body.createDate).format("DD-MM-YYYY HH:mm:ss");
}
console.log('2');
if (req.body.list_id == '') {
listDB.findOne({list: 'Neu'}, function(reqdb, docs) {
if (docs == null) {
listDB.insert({list: 'Neu', index: 1});
listDB.findOne({list: 'Neu'}, function(reqdb, docs) {
console.log('AnlageListID');
console.log(docs._id);
req.body.list_id = docs._id;
});
}
else {
console.log('BestehendeListID');
console.log(docs._id);
req.body.list_id = docs._id;
}
});
}
console.log('3');
if (req.body.status_id == '') {
statusDB.findOne({status: 'offen'}, function(reqdb, docs) {
if (docs == null) {
statusDB.insert({status: 'offen', index: 1});
statusDB.findOne({status: 'offen'}, function(reqdb, docs) {
console.log('AnlageStatusID');
console.log(docs._id);
req.body.status_id = docs._id;
});
}
else {
console.log('BestehendeStatusID');
console.log(docs._id)
req.body.status_id = docs._id;
}
});
}
console.log('4');
console.log('StatusID');
console.log(req.body.status_id);
console.log('ListID');
console.log(req.body.list_id);
todoDB.insert({
todo: req.body.todo,
createDate: req.body.createDate,
endDate: req.body.endDate,
discription: req.body.discription,
comment: req.body.comment,
list_id: req.body.list_id,
priority_id: req.body.priority_id,
section_id: req.body.section_id,
user_id: req.body.user_id,
status_id: req.body.status_id,
company_id: req.body.company_id
});
res.json({message: 'TODO erfolgreich hinzugefĆ¼gt!'});
});
return todoRouter;
};
... and this is the ouput:
1
2
3
4
StatusID
ListID
POST /api/todos 200 76.136 ms - 44
BestehendeListID
M3Xh46VjVjaTFoCM
BestehendeStatusID
48v80B4fbO87c8um
PS: Its a small "project" just for me learing the MEAN Stack so I am using neDB.
If I understand correctly you try to sequentially execute a number of asynchronous calls and introduce checks in the code to validate if previous asynchronous calls have completed. This is not going to work in a general case because your checks may be processed before the asynchronous call goes through. It might work now and then just by chance, but I would not expect even that.
There are standard mechanisms for that. One of them is using promises, another one using async and yet another one if stacking up all callbacks one into another. Below I will demonstrate how to address the problem using async, but the same general idea applies to using promises. Check the async project on Github then the following part-solution will become clear:
var async = require("async")
async.waterfall([
function(next) {
listDB.findOne({list: 'Neu'}, next); // quits on error
},
function(doc, next) {
if (doc) {
return next(null, doc._id);
}
statusDB.insert({status: 'offen', index: 1}, function(err) {
if (err) return next(err); // quit on error
statusDB.findOne({status: 'offen'}, function(err, doc) {
next(err, doc._id); // quits on error
});
});
},
function(id, next) {
// do next step and so on
next();
}
],
// this is the exit function: it will get called whenever an error
// is passed to any of the `next` callbacks or when the last
// function in the waterfall series calls its `next` callback (with
// or without an error)
function(err) {
console.error("Error processing:", err)
});

Dynamic routes with Express.js -- is this even possible?

Every time I update the database with a new menu item, I'm trying to get the routing to update with one more route. Here's my sad little ugly attempt:
Here in app.js, I check the menu database and shazaam...routes are made on the fly at startup. Cool!:
// in app.js //
var attachDB = function(req, res, next) {
req.contentdb = db.content;
req.menudb = db.menu;
req.app = app; // this is the express() app itself
req.page = PageController;
next();
};
db.menu.find({}, function (err, menuitems){
for(var i=0; record = menuitems[i]; i++) {
var menuitem = record.menuitem;
app.all('/' + menuitem, attachDB, function(req, res, next) {
console.log('req from app all route: ',req)
PageController.run(menuitem, req, res, next);
});
}
http.createServer(app).listen(config.port, function() {
console.log(
'\nExpress server listening on port ' + config.port
);
});
});
Not real elegant but it's a proof of concept. Now here's the problem: When I save a new menu item in my Admin.js file, the database get's updated, the router seems to get updated but something about the request just blows up after clicking on a menu link with a dynamically created route
Many things in the request seem to be missing and I feel like there is something fundamental I don't understand about routing, callbacks or perhaps this is just the wrong solution. Here's what the function responsible for creating a new menu item and creating a new route in my Admin.js file looks like:
// in Admin.js //
menuItem: function(req, res, callback) {
var returnMenuForm = function() {
res.render('admin-menuitem', {}, function(err, html) {
callback(html);
});
};
var reqMenudb = req.menudb,
reqContentdb = req.contentdb,
reqApp = req.app,
reqPage = req.page;
if(req.body && req.body.menuitemsubmitted && req.body.menuitemsubmitted === 'yes') {
var data = { menuitem: req.body.menuitem };
menuModel.insert( data, function(err) {
if (err) {
console.log('Whoa there...',err.message);
returnMenuForm();
} else {
// data is inserted....great. PROBLEM...the routes have not been updated!!! Attempt that mimics what I do in app.js here...
reqApp.all('/' + data.menuitem, function(req, res, next) {
// the 2 db references below are set with the right values here
req.contentdb = reqContentdb;
req.menudb = reqMenudb;
next();
}, function(req, res, next) {
reqPage.run(data.menuitem, req, res, next);
});
returnMenuForm();
}
});
} else {
returnMenuForm();
}
},
Saving the data in the admin section works fine. If you console log app.routes, it even shows a new route which is pretty cool. However after refreshing the page and clicking the link where the new route should be working, I get an undefined error.
The admin passes data to my Page controller:
// in PageController.js //
module.exports = BaseController.extend({
name: "Page",
content: null,
run: function(type, req, res, next) {
model.setDB(req.contentdb); /* <-- problem here, req.contentdb is undefined which causes me problems when talking to the Page model */
var self = this;
this.getContent(type, function() {
var v = new View(res, 'inner');
self.navMenu(req, res, function(navMenuMarkup){
self.content.menunav = navMenuMarkup;
v.render(self.content);
});
});
},
getContent: function(type, callback) {
var self = this;
this.content = {}
model.getlist(function(records) {
if(records.length > 0) {
self.content = records[0];
}
callback();
}, { type: type });
}
Lastly, the point of error is here in the model
// in Model.js //
module.exports = function() {
return {
setDB: function(db) {
this.db = db;
},
getlist: function(callback, query) {
this.db.find(query || {}, function (err, doc) { callback(doc) });
},
And here at last, the 'this' in the getlist method above is undefined and causes the page to bomb out.
If I restart the server, everything works again due to my dynamic loader in app.js. But isn't there some way to reload the routes after a database is updated?? My technique here does not work and it's ugly to be passing the main app over to a controller as I'm doing here.
I would suggest two changes:
Move this menu attachment thing to a separate module.
While you're at it, do some caching.
Proof of concept menu db function, made async with setTimeout, you'll replace it with actuall db calls.
// menuitems is cached here in this module. You can make an initial load from db instead.
var menuitems = [];
// getting them is simple, always just get the current array. We'll use that.
var getMenuItems = function() {
return menuitems;
}
// this executes when we have already inserted - calls the callback
var addMenuItemHandler = function(newItem, callback) {
// validate that it's not empty or that it does not match any of the existing ones
menuitems.push(newItem);
// remember, push item to local array only after it's added to db without errors
callback();
}
// this one accepts a request to add a new menuitem
var addMenuItem = function(req, res) {
var newItem = req.query.newitem;
// it will do db insert, or setTimeout in my case
setTimeout(function(newItem){
// we also close our request in a callback
addMenuItemHandler(newItem, function(){
res.end('Added.');
});
}, 2000);
};
module.exports = {
addMenuItem: addMenuItem,
getMenuItems: getMenuItems
}
So now you have a module menuhandler.js. Let's construct it and use it in our app.
var menuHandler = require('./menuhandler');
var app = express();
// config, insert middleware etc here
// first, capture your static routes - the ones before the dynamic ones.
app.get('/addmenuitem', menuHandler.addMenuItem);
app.get('/someotherstaticroute', function(req, res) {
var menu = menuHandler.getMenuItems();
res.render('someview', {menu: menu});
});
// now capture everything in your menus.
app.get('/:routename', function(req, res){
// get current items and check if requested route is in there.
var menuitems = menuHandler.getMenuItems();
if(menuitems.indexOf(req.params.routename) !== -1) {
res.render('myview', {menu: menuitems});
} else {
// if we missed the route, render some default page or whatever.
}
});
app.get('/', function(req, res) {
// ...
});
Now you don't go to db if there were no new updates (since menuitems array is always up to date) so your initial view is rendered faster (for that 1 db call, anyway).
Edit: oh, I just now saw your Model.js. The problem there is that this refers to the object you have returned:
{
setDB: function(db) {
this.db = db;
},
getlist: function(callback, query) {
this.db.find(query || {}, function (err, doc) { callback(doc) });
}
}
So, no db by default. And since you attach something to the app in the initial pageload, you do get something.
But in your current update function, you attach stuff to the new app (reqApp = req.app), so now you're not talking to the original app, but another instance of it. And I think that your subsequent requests (after the update) get the scope all mixed up so lose the touch with the actual latest data.
In your code when you start your server it reads from the menu db and creates your routes. When your menu changes, you do not re-read from db again.
I suggest you do something like the following
app.all('*', function(req, res) {
//read from your menu db and do the the route management yourself
});

Send multiple DB query results to a single view using Express

I have a dashboard view ( dashboard.jade ) that will display two panels with different information, all that info should be retrieved from a database and then sent to the view.
Let's say i have a route file ( document.js ) with two actions defined:
exports.getAllDocuments = function(req, res){
doc = db.model('documents', docSchema);
doc.find({}, function(err, documents) {
if (!err) {
// handle success
}
else {
throw err;
}
});
};
exports.getLatestDocumentTags = function(req, res){
tags = db.model('tags', tagSchema);
tags.find({}, function(err, docs) {
if (!err) {
// handle success
}
else {
throw err;
}
});
};
These functions would only serve the porpuse of retrieving data from the database.
Now i would like to send that data to the dashboard view from my dashboard.js route file under exports.index function where i render my dashboard view.
The problem is, since the db calls will be async i wouldn't have access to the data before i could call the view.
I guess i could have an action that simply did all my db calls and through callbacks deliver all the data at once to the view but that would make my data retrieval actions not reusable.
I'm really confused on how to tackle this problem correctly, probably i'm getting this async thing all wrong. Can someone give me some hints on how to do this properly ?
Here's something to pique your interest.
//Check out the async.js library
var async = require('async');
//Set up your models once at program startup, not on each request
//Ideall these would be in separate modules as wel
var Doc = db.model('documents', docSchema);
var Tags = db.model('tags', tagSchema);
function index(req, res, next) {
async.parallel({ //Run every function in this object in parallel
allDocs: async.apply(Doc.find, {}) //gets all documents. async.apply will
//do the equivalent of Doc.find({}, callback) here
latestDocs: async.apply(Tags.find, {})
], function (error, results) { //This function gets called when all parallel jobs are done
//results will be like {
// allDocs: [doc1, doc2]
// latestDocs: [doc3, doc4]
// }
res.render('index', results);
});
}
exports.index = index;
};
Try some more tutorials. If you haven't had the "a ha" moment about how async programming works in node, keep going through guided, hand-held tutorials before trying to write brand new programs without guidance.
//Check out the async.js library and mangoose model
var mongoOp = require("./models/mongo");
var async = require('async');
router.get("/",function(req,res){
var locals = {};
var userId = req.params.userId;
async.parallel([
//Load user Data
function(callback) {
mongoOp.User.find({},function(err,user){
if (err) return callback(err);
locals.user = user;
callback();
});
},
//Load posts Data
function(callback) {
mongoOp.Post.find({},function(err,posts){
if (err) return callback(err);
locals.posts = posts;
callback();
});
}
], function(err) { //This function gets called after the two tasks have called their "task callbacks"
if (err) return next(err); //If an error occurred, we let express handle it by calling the `next` function
//Here `locals` will be an object with `user` and `posts` keys
//Example: `locals = {user: ..., posts: [...]}`
res.render('index.ejs', {userdata: locals.user,postdata: locals.posts})
});

rendering results of multiple DB/mongoose queries to a view in express.js

given the async nature of mongoose (or sequelize, or redis) queries, what do you do when you have multiple queries you need to make before rendering the view?
For instance, you have a user_id in a session, and want to retrieve some info about that particular user via findOne. But you also want to display a list of recently logged in users.
exports.index = function (req, res) {
var current_user = null
Player.find({last_logged_in : today()}).exec(function(err, players) {
if (err) return res.render('500');
if (req.session.user_id) {
Player.findOne({_id : req.session.user_id}).exec(function(err, player) {
if (err) return;
if (player) {
current_user = player
}
})
}
// here, current_user isn't populated until the callback fires
res.render('game/index', { title: 'Battle!',
players: players,
game_is_full: (players.length >= 6),
current_user: current_user
});
});
};
So res.render is in the first query callback, fine. But what about waiting on the response from findOne to see if we know this user? It is only called conditionally, so I can't put render inside the inner callback, unless I duplicate it for either condition. Not pretty.
I can think of some workarounds -
make it really async and use AJAX on the client side to get the current user's profile. But this seems like more work than it's worth.
use Q and promises to wait on the resolution of the findOne query before rendering. But in a way, this would be like forcing blocking to make the response wait on my operation. Doesn't seem right.
use a middleware function to get the current user info. This seems cleaner, makes the query reusable. However I'm not sure how to go about it or if it would still manifest the same problem.
Of course, in a more extreme case, if you have a dozen queries to make, things might get ugly. So, what is the usual pattern given this type of requirement?
Yep, this is a particularly annoying case in async code. What you can do is to put the code you'd have to duplicate into a local function to keep it DRY:
exports.index = function (req, res) {
var current_user = null
Player.find({last_logged_in : today()}).exec(function(err, players) {
if (err) return res.render('500');
function render() {
res.render('game/index', { title: 'Battle!',
players: players,
game_is_full: (players.length >= 6),
current_user: current_user
});
}
if (req.session.user_id) {
Player.findOne({_id : req.session.user_id}).exec(function(err, player) {
if (err) return;
if (player) {
current_user = player
}
render();
})
} else {
render();
}
});
};
However, looking at what you're doing here, you'll probably need to look up the current player information in multiple request handlers, so in that case you're better off using middleware.
Something like:
exports.loadUser = function (req, res, next) {
if (req.session.user_id) {
Player.findOne({_id : req.session.user_id}).exec(function(err, player) {
if (err) return;
if (player) {
req.player = player
}
next();
})
} else {
next();
}
}
Then you'd configure your routes to call loadUser wherever you need req.player populated and the route handler can just pull the player details right from there.
router.get("/",function(req,res){
var locals = {};
var userId = req.params.userId;
async.parallel([
//Load user Data
function(callback) {
mongoOp.User.find({},function(err,user){
if (err) return callback(err);
locals.user = user;
callback();
});
},
//Load posts Data
function(callback) {
mongoOp.Post.find({},function(err,posts){
if (err) return callback(err);
locals.posts = posts;
callback();
});
}
], function(err) { //This function gets called after the two tasks have called their "task callbacks"
if (err) return next(err); //If an error occurred, we let express handle it by calling the `next` function
//Here `locals` will be an object with `user` and `posts` keys
//Example: `locals = {user: ..., posts: [...]}`
res.render('index.ejs', {userdata: locals.user,postdata: locals.posts})
});
Nowadays you can use app.param in ExpressJS to easily establish middleware that loads needed data based on the name of parameters in the request URL.
http://expressjs.com/4x/api.html#app.param

Resources