I have created a function here :
router.get('/restful',function(req,res,next){
var resultArray=[];
mongo.connect(url,function(err,db){
assert.equal(null,err);
var cursor=db.collection('users').find();
cursor.forEach(function(doc,err){
assert.equal(null,err);
resultArray.push(doc);
req.session.resultArray=resultArray;
},function(){
db.close();
res.render('restful',{items:resultArray});
});
});
});
I have created a method/restful. The "restful" is a page.
All I want is when I load this page, the resultArray gets displayed, but it's not displayed at the first load.
It's displaying after we navigate it for the second time. So the data is not being retrieved.
How can I solve this issue?
Please help me to find the solution.
I don't believe this is due to the nature of how sessions work in node.js, but you may want to reference: https://stormpath.com/blog/everything-you-ever-wanted-to-know-about-node-dot-js-sessions
I would move req.session.resultArray out of the forEach as there is no benefit to overwriting the session multiple times when you're already building the array for its value, you can just set it later after you've built the entire array.
This shouldn't be an issue with needing to invoke a redirect to view the new session data because we're not even technically messing with accessing a cookie or session (yet), we're using res.render() to make a variable items expose an internal variable for resultArray within our view. Although if you want to play with that you can use res.redirect('/restful') to invoke a redirect where data should already be initialized.
I would look at something like this
router.get('/restful', function( req, res, next ) {
var resultArray = [];
mongo.connect(url, function( err, db ) {
assert.equal(null, err);
var cursor = db.collection('users').find();
cursor.forEach(function(doc, err){
assert.equal(null, err);
resultArray.push(doc);
});
db.close();
});
req.session.resultArray = resultArray;
// this should contain your
// session data
console.log(resultArray);
res.render('restful', {items:resultArray});
next();
});
But I would like to note, the problem you are facing is not with session data not saving or being accessible so to speak, but rather that at the time of res.render() your resultArray does not appear to be built yet as you're only exposing this variable to your view.
I would play around with logging your resultArray and the answer should become clear to you.
Related
I am building an integration with Express Session and I am trying to authenticate the user in a separate webhook. So I need a way to update the user session outside of the request since it would be a different request.
What I did is I passed the session ID to the new request, and use MongoClient to update the session in the database. I remember Express Session only stores the ID on the client-side and the data is store on the database, so I assume updating the database would do it. But that doesn't seem to be the case. I can clearly see that the session on MongoDB is all updated but I kept getting the outdated data in req.session.data.
Here's what I've done
So the first thing I tried is to use the req.session.reload() method like these:
Updating express-session sessions
change and refresh session data in Node.js and express-session
But it is still giving me outdated data as if the function did nothing (it did print out logs so I assume it did run).
I tried using this that uses store.get() method from Express Session but it is giving me undefined session.
Express load Session from Mongo with session_id
So as a last resort I use MongoClient to get the session data directly and update the session with the data obtained. I use async for the route handler and await the MongoClient to get the data. It doesn't wait for the await and just kept throwing undefined. I thought it's my code that's wrong but I am able to get user data with the code and it did wait for the MongoClient to get the data, but somehow it is not working for session data.
Here's part of my code:
app.router.get('/login', async function (req, res, next) {
req.session.auth = await mongo.getCookie(req.session.id).auth;
req.session.user = await mongo.getCookie(req.session.id).user;
console.log('Login Action', req.session.id, req.session.auth, req.session.user);
}
module.exports.getCookie = async function getCookie(identifier) {
try {
let database = client.db('database');
let collection = await database.collection('sessions');
let result = await collection.findOne({ _id: identifier }, function(err, res) {
if (err) throw err;
return res;
});
return result;
}
catch (err) {
console.error(err);
return null;
}
}
Here's other answers that I've check
This one only update the expiration so I assume its not going to work for me, I did set the resave to false so that it doesn't try to save every single request, since its saving to the database I assume it has nothing to do with updating and I have tried setting it to true and nothing happened.
Renewing/Refreshing Express Session
And in this it uses socket.io and store.get() so it's not going to work as well.
How to access session in express, outside of the req?
Can someone guide me on how do I get Express Session to update from the database or is there a way to get the data from the database and update the session that way?
So I did eventually figured it out, turns out the session was not updated from the database. Looks like it has a local copy in the cache, but I was under the impression that the express session only uses the database.
I know for a fact that my data is indeed in the database but not in the cache or wherever the local copy of the express session is stored. So the easiest way to get pass this problem is to update the local copy with the data on the database.
And I created this function to update the session data
async function sessionUpdate(req) {
try {
// get the data on the database
let tempSession = await mongo.getCookie(req.session.id);
// if the data exist, we can copy that to our current session
if (tempSession) {
req.session.auth = tempSession.auth;
req.session.user = tempSession.user;
// you can do other stuff that you need to do with the data too
if (req.session.health == null || req.session.health == undefined || typeof req.session.health === 'undefined') {
req.session.health = 0;
}
// This update and save the data to the session
req.session.save( function(err) {
req.session.reload( function (err) {
//console.log('Session Updated');
});
});
}
else if (!tempSession) {
req.session.auth = false;
req.session.user = null;
req.session.ip = request.connection.remoteAddress;
}
}
catch (err) {
console.error(err);
}
}
Now that we have a function that can update the session data, we need to implement it everywhere. I use middleware to do that, you can use other methods too but this must be called before the route or the session logic.
// middleware with no mounted path so it is used for all requests
receiver.router.use(async function (req, res, next) {
try {
await sessionUpdate(req);
}
catch (err) {
console.error(err);
}
next();
});
It's not exactly a good/efficient way of doing it... that's a lot of things to run before even getting to the route so I would assume it would bring the performance down a bit... But this is how I get it to work and if anyone can come up with some better idea I would very much appreciate it 🙌
Good day all!
I have an ajax method that sends data to the backend and the backend does operations with the data base (Rest API I think it's called).
The problem is, even thought I have no errors on frontend or backend, the operation I'm trying to complete doesn't take place. Hope you can help, thanks in advance.
//Front-end (jQuery ajax)
$(document).on('click', '.remove-product', function () {
//Get the ID variable inside the element we press
var idEl = $(this).closest('.item-box');
var id = idEl.find('.product-id').text();
console.log(id);
$.ajax({
type:'DELETE',
url: '/dashboard/:' + id,
success: ajaxMessaging('green', 'Deleted a record')
});
});
//Backend
router.delete('/dashboard/:id', (req, res)=>{
idStr = req.params.id;
id = idStr.replace(":", ""); //Because : is sent through ajax
console.log(id);
User.deleteOne({_id : mongoose.Types.ObjectId(id)}, (err)=>{
if(err) console.log(err);
});
});
Please note that I'm trying to delete an array index, but since every element has a unique id (right?) I'm trying to delete the index by id.
This is a typical route in node.js that has a pseudo-code to connect to a database, get some data using a query and then pass them to a page to be rendered,
router.get('/', function(req, res){
db-connect(function(err, db) {
if (err) {
return console.log('error');
}
db.query('select * from table', function(err, results){
if (err) {
return console.log('error');
}
res.render('index',{
'title':'my title',
'pageHeader': 'my header',
'results': results //dynamic ???
});
});
}); //connect
});//router get
I am using this pseudo-code to ask a general question :
The results data are dynamic, maybe the query will take a while, so the results do not get to the page fast, so I guess the rendering will also take a while.
How can I render static data immediatly (title and pageHeader) and the dynamic part (results) as soon as it is ready ?
Do I have to use another function or another syntax?
Thank you
res.render populates your template and sends it to the client (browser). You cannot send "a bit more" when it's ready at a later stage.
Either make the client wait for the data, or send your title and header first, and use XHR (javascript) on the browser to get the rest.
I'm using nodejs and express and I have a navigation menu that is built using data that is in mongodb currently I'm just making a call to the database to get a list of companies and passing that back inside each route. There doesn't seem to be a way to store this information in localstorage client side. So I"m wondering what is the most effective way to handle this situation. Sample of my code
admin.get('/', function(res, resp){
mongodb.connect(url, function(err, db){
var collection = db.collection('companies')
collection.find({}).toArray(function(err, companies){
res.render('adminview', {companies:companies})//once the page is rendered I would like to save the company list to localstorage.
})
})
})
admin.get('/:company', function(res, resp){
/* repeating code from above because I need this list */
mongodb.connect(url, function(err, db){
var collection = db.collection('companies')
collection.find({}).toArray(function(err, companies){
/* more code to do stuff to render the company page */
res.render('companyadminview', {companies:companies, company:company})
}) })
I could be going about this the wrong way I'm new to web development this feels wrong to me but can't figure out a different way.
So, first off you should be able to store it in localstorage or sessionstorage just fine, unless you're targeting browsers that don't support it.
That said, I think it's best not to, as the fact that you're storing it in the DB implies that it changes with enough frequency that you will get buggy clientside behavior if you cache it there for too long.
Instead, I'd just setup a middleware and attach it to the locals object on a per request basis, unless you want to do some kind of cache on the server:
app.use(function(req, res, next) {
mongodb.connect(url, function(err, db){
if (err) return next(err);
var collection = db.collection('companies')
collection.find({}).toArray(function(err, companies){
if (err) return next(err);
res.locals.companies = companies;
next();
});
});
});
I'm trying to use app.render() to display a jade file in the browser. In the following code, the html is displayed to the console correctly, but the browser never shows the related file.
app.render('unavailable', {title: 'Unavailable'}, function(err, html){
console.log(html);
});
EDIT:
I have this handler:
app.get('/unavailable', display.unavailable);
Then beneath this code in the same file (app.js) I have this:
sql.open(connStr, function(err, sqlconn){
if(err){
console.error("Could not connect to sql: ", err);
else
conn = sqlconn; //save the sql connection globally for all client's to use
});
So, what I want to happen is when the err happens with the SQL connection, the /unavailable handler is executed and a static html page is displayed that says the service is down. However, because the error occurs on the server, and not the client, I don't have access to a response object at that time. I'm trying to artifically manufacture the client 'redirecting' to /unavailable in their browser to see the message.
Obviously you don't send the html to the browser. Use res.render inside a route without callback, i.e.
res.render('unavailable', {title: 'Unavailable'});
or send the result of rendering like here:
app.render('unavailable', {title: 'Unavailable'}, function(err, html){
console.log(html);
res.send(html);
});
Read more about the difference here:
What's the difference between "app.render" and "res.render" in express.js?
save a global var sqlOK = false, set it in sql.open callback, and redirect to /unavailable if you get a request while sqlOK is not true. you were also missing brackets around the else statement.
var sqlOK = false;
app.get('/unavailable', display.unavailable);
app.get('*', function(req, res, next){
if(!sqlOK){
return res.redirect('/unavailable');
//return res.send(500)
};
next();
});
sql.open(connStr, function(err, sqlconn){
if(err){
console.error("Could not connect to sql: ", err);
} else {
conn = sqlconn; //save the sql connection globally for all client's to use
sqlOK = true
}
});