Storing variable in session with ejs and node js - node.js

Im trying to think of a solution to the following issue:
I have a variable that needs to be separate for every unique visitor on the page. Its just simple list that is filled when a visitor clicks on some items on the page. It would be empty for each unique visit.
Currently Im keeping that in the backend, it is pretty bad solution as everyone currently using the app is adding to the same list
The backend code:
thelist=[]
app.get("/show/:id", function (req, res) {
var id = req.params.id;
thelist.push(id)
console.log(thelist);
res.redirect('/')
});
app.get("/run", function(req, res) {
res.render("data", {data: thelist})
thelist = []
});
The main point is that list thelist is then passed to a python script which is also connected via node.js into the app. Therefore i need that variable to stay in the backend.
Also I was thinking of keeping it simple, so I don't want to force users to create account to use the app, so some sort of cookies/cache/any other form of session storage is what im looking for.

You can create a session map(like hashmaps). I have integrated the same in one of my projects and it is working flawlessly. Below is the code for it and how you can access it:
hashmap.js
var hashmapSession = {};
exports.auth = auth = {
set : function(key, value){
hashmapSession[key] = value;
},
get : function(key){
return hashmapSession[key];
},
delete : function(key){
delete hashmapSession[key];
},
all : function(){
return hashmapSession;
}
};
app.js
var hashmap = require('./hashmap');
var testObj = { id : 1, name : "john doe" };
hashmap.auth.set('test', testObj);
hashmap.auth.get('test');
hashmap.auth.all();
hashmap.auth.delete('test');
You can use this approach as a session storage for your node application. Keep in mind that this storage will only be persistent for the current session only.

The best solution here would be express session (https://github.com/expressjs/session)
As I see your code I can figure out you are using express. And if you are using express that is enough for below code.
Here is the easiest way with minimal code to achieve what you are looking for:
Example:
app.set('oneSetting', 'one');
console.log(app.settings.oneSetting);
OR
app.set('port', 3000);
app.get('port'); //3000
You can access the same variables in the req object too, example:
req.app.get('port')

Why don't you use express-session.
You can use it like the following :
App.js
var app = express();
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
cookie: { secure: true }
}))
Controller.js
//Sets the id in the session object.
app.get('/show/:id', function (request, response, next) {
request.session.id = id;
})
//Retrieve the id from the session object.
app.get('/showMyId',function(request,response,next)){
response.send(request.session.id;);
})
This way you do not have to manage the global list and all your ids would be present inside their respective session object.
This approach uses a cookie session , there are also alternative strategies available for storing your session object like a proper storage server.

From my understanding you are trying to count no.of unique visitors for your application.
You can use simple cookie to track use and set expiry long time so cookie is not erased.
Express had cookie parser module which will automatically parse cookie.
thelist={}
var express = require('express')
var cookieParser = require('cookie-parser')
var app = express()
app.use(cookieParser())
app.get("/show/:id", function (req, res) {
var visitorId = req.cookies.visitorid;
var id = req. params.id
if(!req.cookies[id]){
///create unique id like GUID or something which allows you to uniquely identify user
if(!visitorId){
visitorId = '<GUID>';
}
if(!thelist[visitorId]){
thelist[visitorId] = [];
}
thelist[visitorId].push(id);
console.log(thelist);
}
res.cookie('visitorid',visitorId, { maxAge: 900000, httpOnly: true }); //You can extend max age as required. Setting up every time will also extend expiry.
res.cookie(id,'visited', { maxAge: 900000, httpOnly: true }); //set cookie with id saying this is allready visited
res.redirect('/')
});
app.get("/run", function(req, res) {
res.render("data", {data: thelist})
thelist = []
});

you can use sessionstorage on your window session
sessionStorage.setItem(KEY,VALUE);

Related

node js server propplem

const express = require("express");
const app = express();
var i = new Number;
i=0;
app.get("/", function(req, res){
i++
console.log(i);
});
app.listen(8080);
I created a very small node js project. I have a problem. when I create a variable like above, it doesn't evaluate for each user separately. that is, when a user requests a get, I want it to be 1 each time.
Sample
my problem is that when a jack user enters a site, if he doesn't log out, someone who enters the site's home page from another device enters his account with jack.
how can I do that?
The simplest answer for your question is to simply declare and increment the variable inside the function passed to app.get, but I'm going to assume that you would like a situation where, for a given user's series of requests, the number will increment.
The simplest way to do this is using a server side session, which is provided by the express-session library. Additionally, in order for this to work, you need to call res.end() in order to send the cookie associated with the server session back to the user's browser. More information on sessions generally can be found here.
Below is code to replicate the intent of what you have there, but incrementing for each request from a unique browser instance (identified by the same cookie value associated with the server session):
const express = require('express');
const session = require('express-session');
const app = express();
app.use(session({
resave: false,
saveUninitialized: true,
secret: 'secret',
cookie: {
maxAge: 60000
}
}));
app.get('/', function(req, res){
if (!req.session.value) {
req.session.value = 0;
}
req.session.value++;
console.log(req.session.value);
res.end();
});
app.listen(8080);

Accessing session token from other routers in node js

I am trying to access my session token from other routes after setting it in a route. I am currently unsuccessful. Following the relevant code of the three files.
server.js: It calls the routes thermostats, login and also sets session token.
var session = require('express-session');
var app = express();
app.use(session({secret: 'keyboard cat',cookie: { secure: true }}))
var router = express.Router();
var thermostats = require('./api/routes/thermostats')(router, app, session);
require('./api/routes/login')(router, app, session, thermostats);
login.js: When the user goes to localhost:3000/login/, the login token needs to be saved in the session
module.exports = function(router,app, session, thermostats){
router.get('/login/', function(req, res) {
list(req, res) //response of this function has session which needs to be saved.
console.log(res.session)
app.use(session(res.session)) //trying to save the res.session as session token
});
}
thermostat.js: Needs to access the session token before can display any information.
module.exports = function(router,app){
router.get('/thermostats/', function(req, res) {
console.log(req.session) //Set to default/null values and not the updated session values
});
}
It might be something small but I cannot figure out the issue. I would appreciate any help.
Thank you
Express-session should automatically save the session, based on the configuration.
Looking at the 'resave' config option in the express-session docs:
resave
Forces the session to be saved back to the session store, even if the session was never modified during the request. Depending
on your store this may be necessary, but it can also create race
conditions where a client makes two parallel requests to your server
and changes made to the session in one request may get overwritten
when the other request ends, even if it made no changes (this behavior
also depends on what store you're using).
This is by default, true, so it should already start working without you needing to add app.use(session(res.session).
Edit: You will be able to save to the session by adding fields to the req.session object:
router.get('/login/', function(req, res) {
getDataFromExternalApi(req, function(err, apiResponse) {
var data = apiResponse.data;
req.session.data = data;
// if resave option is true, this should automatically save to the session store after this request is done.
});
});
Generally, you shouldn't be using app.use in your request handlers. Those are generally reserved for setting up the server, as it defines what middleware express uses.

NodeJS: session variables shared for all clients (express-session)

I'm using express-session for handling session variables in a NodeJS Application. I use session variables for handling login-authorization, for saving logged user information and other things.
The thing is that this session variables are being shared for all clients, it looks like they are working as NodeJS instance variables instead of session variables for every client. What I want to have is session variables working as they work in PHP.
This application is retrieving all the data from web services in a Laravel Back-End application.
This is my code:
Initializing session:
var sessionHelper = require('./helpers/session-helper');
var session = require('express-session');
app.use(session({
secret: config.SESSION_SECRET,
resave: false,
saveUninitialized: true,
cookie: {secure: true}
}));
sessionHelper.init(session);
global.SESSION = sessionHelper;
session-helper module:
var _session = null;
module.exports = {
init: function (session) {
_session = session;
},
has: function (name) {
return ((_session[name]) ? true : false);
},
get: function (name) {
return _session[name];
},
set: function (name, value) {
_session[name] = value;
},
clear: function (name) {
_session[name] = undefined;
}
};
Using session variables:
SESSION.set('hotels', response.hotels);
SESSION.get('hotels');
Thanks,
The problem is that you've globally cached a specific instance of session in your helper object. As a general practice, that's not a good idea unless you are very sure about how that object's lifecycle and state are managed.
The way that express sessions work is that the express middleware maintains a separate instance of session per request. You should be accessing that session typically in the body of a request:
app.get('/', function(req, res, next) {
var sess = req.session;
// sess will have values specific to each unique browser session
console.log('This session has an id of ', sess.id);
});
If you still feel you want to setup a helper, you can make that available for every request by configuring Express with the use method before your app.get or any other router methods - here is a rough idea how:
// This should be AFTER the app.use statement for express sessions
app.use(function (req, res, next) {
var sessionHelper = require('./helpers/session-helper');
sessionHelper.init(req.session);
req.sessionHelper = sessionHelper;
next();
})
Now, in any subsequent route handler code, you will find that req.sessionHelper is available for use. This is because you've told Express to first add your helper to the request object for ALL requests. So, this will work:
app.get('/', function(req, res, next) {
console.log('Session ID: ', req.sessionHelper.get('id'));
});
Just remember that you are still responsible for session storage. You need to combine express-sessions with a store (like connect-redis or connect-mongo) in order to persist session-data between restarts. The full list is here: https://github.com/expressjs/session#compatible-session-stores

express session store user agent

I set up session management in my node js/ express js website successfully. I stores session data in mongo db. I want the session to be valid for the users who log in for a couple of weeks. The code is as follows:
var cookieParser = require('cookie-parser');
var session = require('express-session');
var MongoStore = require('connect-mongo')(session);
app.use(cookieParser());
app.use(session({
store: new MongoStore({ mongoose_connection: db }),
secret: 'cookie_secret',
cookie: { maxAge: null }
}));
It works fine for normal users, but my problem is with web crawlers such as google bots and facebook bots. I still want them to crawl my website but I don't want their sessions to be stored in my mongo db. It's taking up lots of space and storage is increasing daily which costs me money.
How to selectively choose which sessions to be stored in the db. I can check for req.headers['user-agent'], but where to use it in my code? How to tell express-session not to store session sometimes?
You can use the session middleware conditionally, based on the User-Agent header. A simple example:
var sessionMiddleware = require('express-session')({
...configuration here...
});
app.use(function(req, res, next) {
var ua = req.get('user-agent');
// If the User-Agent header contains the string "Googlebot",
// skip the session middleware.
if (/Googlebot/.test(ua)) {
req.session = {}; // perhaps a bit too simple?
return next();
}
return sessionMiddleware(req, res, next);
});
It would depend on your actual use of req.session if the code above works, or if you need to mock req.session a bit better (for instance, if you use any of the req.session methods in your code,
you may need to mock those too).

How do I redirect in expressjs while passing some context?

I am using express to make a web app in node.js. This is a simplification of what I have:
var express = require('express');
var jade = require('jade');
var http = require("http");
var app = express();
var server = http.createServer(app);
app.get('/', function(req, res) {
// Prepare the context
res.render('home.jade', context);
});
app.post('/category', function(req, res) {
// Process the data received in req.body
res.redirect('/');
});
My problem is the following:
If I find that the data sent in /category doesn't validate, I would like pass some additional context to the / page. How could I do this? Redirect doesn't seem to allow any kind of extra parameter.
There are a few ways of passing data around to different routes. The most correct answer is, of course, query strings. You'll need to ensure that the values are properly encodeURIComponent and decodeURIComponent.
app.get('/category', function(req, res) {
var string = encodeURIComponent('something that would break');
res.redirect('/?valid=' + string);
});
You can snag that in your other route by getting the parameters sent by using req.query.
app.get('/', function(req, res) {
var passedVariable = req.query.valid;
// Do something with variable
});
For more dynamic way you can use the url core module to generate the query string for you:
const url = require('url');
app.get('/category', function(req, res) {
res.redirect(url.format({
pathname:"/",
query: {
"a": 1,
"b": 2,
"valid":"your string here"
}
}));
});
So if you want to redirect all req query string variables you can simply do
res.redirect(url.format({
pathname:"/",
query:req.query,
});
});
And if you are using Node >= 7.x you can also use the querystring core module
const querystring = require('querystring');
app.get('/category', function(req, res) {
const query = querystring.stringify({
"a": 1,
"b": 2,
"valid":"your string here"
});
res.redirect('/?' + query);
});
Another way of doing it is by setting something up in the session. You can read how to set it up here, but to set and access variables is something like this:
app.get('/category', function(req, res) {
req.session.valid = true;
res.redirect('/');
});
And later on after the redirect...
app.get('/', function(req, res) {
var passedVariable = req.session.valid;
req.session.valid = null; // resets session variable
// Do something
});
There is also the option of using an old feature of Express, req.flash. Doing so in newer versions of Express will require you to use another library. Essentially it allows you to set up variables that will show up and reset the next time you go to a page. It's handy for showing errors to users, but again it's been removed by default. EDIT: Found a library that adds this functionality.
Hopefully that will give you a general idea how to pass information around in an Express application.
The easiest way I have found to pass data between routeHandlers to use next() no need to mess with redirect or sessions.
Optionally you could just call your homeCtrl(req,res) instead of next() and just pass the req and res
var express = require('express');
var jade = require('jade');
var http = require("http");
var app = express();
var server = http.createServer(app);
/////////////
// Routing //
/////////////
// Move route middleware into named
// functions
function homeCtrl(req, res) {
// Prepare the context
var context = req.dataProcessed;
res.render('home.jade', context);
}
function categoryCtrl(req, res, next) {
// Process the data received in req.body
// instead of res.redirect('/');
req.dataProcessed = somethingYouDid;
return next();
// optionally - Same effect
// accept no need to define homeCtrl
// as the last piece of middleware
// return homeCtrl(req, res, next);
}
app.get('/', homeCtrl);
app.post('/category', categoryCtrl, homeCtrl);
I had to find another solution because none of the provided solutions actually met my requirements, for the following reasons:
Query strings: You may not want to use query strings because the URLs could be shared by your users, and sometimes the query parameters do not make sense for a different user. For example, an error such as ?error=sessionExpired should never be displayed to another user by accident.
req.session: You may not want to use req.session because you need the express-session dependency for this, which includes setting up a session store (such as MongoDB), which you may not need at all, or maybe you are already using a custom session store solution.
next(): You may not want to use next() or next("router") because this essentially just renders your new page under the original URL, it's not really a redirect to the new URL, more like a forward/rewrite, which may not be acceptable.
So this is my fourth solution that doesn't suffer from any of the previous issues. Basically it involves using a temporary cookie, for which you will have to first install cookie-parser. Obviously this means it will only work where cookies are enabled, and with a limited amount of data.
Implementation example:
var cookieParser = require("cookie-parser");
app.use(cookieParser());
app.get("/", function(req, res) {
var context = req.cookies["context"];
res.clearCookie("context", { httpOnly: true });
res.render("home.jade", context); // Here context is just a string, you will have to provide a valid context for your template engine
});
app.post("/category", function(req, res) {
res.cookie("context", "myContext", { httpOnly: true });
res.redirect("/");
}
use app.set & app.get
Setting data
router.get(
"/facebook/callback",
passport.authenticate("facebook"),
(req, res) => {
req.app.set('user', res.req.user)
return res.redirect("/sign");
}
);
Getting data
router.get("/sign", (req, res) => {
console.log('sign', req.app.get('user'))
});
we can use express-session to send the required data
when you initialise the app
const express = require('express');
const app = express();
const session = require('express-session');
app.use(session({secret: 'mySecret', resave: false, saveUninitialized: false}));
so before redirection just save the context for the session
app.post('/category', function(req, res) {
// add your context here
req.session.context ='your context here' ;
res.redirect('/');
});
Now you can get the context anywhere for the session. it can get just by req.session.context
app.get('/', function(req, res) {
// So prepare the context
var context=req.session.context;
res.render('home.jade', context);
});
Here s what I suggest without using any other dependency , just node and express, use app.locals, here s an example :
app.get("/", function(req, res) {
var context = req.app.locals.specialContext;
req.app.locals.specialContext = null;
res.render("home.jade", context);
// or if you are using ejs
res.render("home", {context: context});
});
function middleware(req, res, next) {
req.app.locals.specialContext = * your context goes here *
res.redirect("/");
}
You can pass small bits of key/value pair data via the query string:
res.redirect('/?error=denied');
And javascript on the home page can access that and adjust its behavior accordingly.
Note that if you don't mind /category staying as the URL in the browser address bar, you can just render directly instead of redirecting. IMHO many times people use redirects because older web frameworks made directly responding difficult, but it's easy in express:
app.post('/category', function(req, res) {
// Process the data received in req.body
res.render('home.jade', {error: 'denied'});
});
As #Dropped.on.Caprica commented, using AJAX eliminates the URL changing concern.
Update 2021:
i tried url.format and querystring and both of them are deprecated, instead we can use URLSearchParams
const {URLSearchParams} = require('url')
app.get('/category', (req, res) =>{
const pathname = '/?'
const components ={
a:"a",
b:"b"
}
const urlParameters = new URLSearchParams(components)
res.redirect(pathname + urlParameters)
})
I use a very simple but efficient technique
in my app.js ( my entry point )
I define a variable like
let authUser = {};
Then I assign to it from my route page ( like after successful login )
authUser = matchedUser
It May be not the best approach but it fits my needs.
app.get('/category', function(req, res) {
var string = query
res.redirect('/?valid=' + string);
});
in the ejs you can directly use valid:
<% var k = valid %>

Resources