Sharing a variable between 2 routes express - node.js

I wanted to know if there is a way to share variables between 2 routes in expressJS without declaring it as a global. Consider I have the following routes:
exports.routeOne = (req,res) => {
var myVariable='this is the variable to be shared';
}
exports.routeTwo = (req,res) => {
//need to access myVariable here
}
How can this be done ?

===============
Note
This answer is answering the wrong question, but it might still be useful for others who also misinterpreted the question like I did, so I'm keeping it here for now.
===============
In order for the variable to exist it would have to first execute routeOne and then execute routeTwo. This is a pretty common scenario with express. To get the details on how this works read up on middleware (http://expressjs.com/en/guide/writing-middleware.html) and understand that each route is middleware.
The common pattern solution here is to add a new property to either the req or res object that stores your variable. Then you tell the route to call the next middleware. The next middleware has access to the same req and res so it also has access to the property and value that you just stored.
There is nothing wrong with this practice as most middleware does it. For example the body-parser middleware (https://www.npmjs.com/package/body-parser).
Here is an example of how your code might run:
routes.js
exports.routeOne = (req,res,next) => {
req.myVariable='this is the variable to be shared'
next()
}
exports.routeTwo = (req,res) => {
//need to access myVariable here
console.log(req.myVariable)
}
index.js
const express = require('express')
const routes = require('./routes.js)
const app = express()
app.use(routes.routeOne)
app.use(routes.routeTwo)
app.listen(3000)

One of the awesome things about NodeJS is that you can store state in variables. The process that runs your NodeJS server continues to run without restarting for each request that comes in. So any variables you store using GLOBAL.myVarName or variables that you store within the scope of a module (a single JavaScript file) will persist... until the process quits. The process could quit if the server goes down or if an Error or other exception is throw that is not caught. You can set up a process manager to keep it running, but your in memory variables are now lost.
I would recommend using variables that are scoped at a higher level for caching, but if the data is important it should be stored to and read from a database as needed.
Here is an example of your routes.js module. Any variable you declare outside of functions are scoped to this file. In other words, the variables are global to this file. You can read up more on that at https://nodejs.org/dist/latest-v8.x/docs/api/modules.html and https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures.
routes.js
let variableScopedToModule = 0
exports.routeOne = (req, res) {
variableScopedToModule++
}
exports.routeTwo = (req, res) {
console.log(variableScopedToModule)
}
If it is important that you never loose the variables state, and assuming you are not running the server on multiple processes for load balancing, then you could do something like this:
routes.js
const db = require('./my-db-controller')
let cachedValue
exports.routeOne = async (req, res) {
if (cachedValue === undefined) cachedValue = await db.getValue()
cachedValue++
db.setValue(cachedValue)
}
exports.routeTwo = (req, res) {
console.log(cachedValue)
}
This second solution is more complex because you'll need to understand promises as well as async and await. Also this solution will not work if you are using a load balencer unless you use something like Redis. Alternatively you can just read and write to the database for each request instead of using in memory caching.

For user-specific data that you want to persist from one request to the next, you can't just store data in simple server-side variables because server-side variables are freely shared among all users who make requests of your server.
The usual way to store server-side data that is user-specific is to use a session object. In a nutshell, when a user makes a request, some session middleware (which runs on every request) checks to see if a session cookie exists. If it does not, then one is created. The session cookie servers to uniquely identify that particular user when they make future requests.
Once you have a unique session cookie, the session middleware can create a session object that corresponds with that session cookie. Typically, there is a unique ID in the session cookie that servers as a lookup key for the session object.
Now, whenever that user makes a request of your server, the session middleware will look at the session cookie, get the encrypted session id out of it, decrypt the id, look up the session object that corresponds to that id and then make that session object available to your request handlers, usually in req.session. Your request handlers then have full access to the session object and can add, remove or modify your own properties on that session object.
express-session is common piece of middleware that is used for implementing sessions with nodejs/express. There are plenty of examples for how to use it in the documentation so I won't repeat that all here. Here's one such example in the doc.
By default, express-session uses a memory store (which just means the session objects are kept in memory). This can be fine for simple uses, but it has some limitations and express-session does not recommend it for production use. One major limitation of keeping the sessions in memory is that if the server crashes or restarts, all session data is lost. There are dozens of add-in object stores for express-session, most of which store data more durably such as in a database on disk.

Related

Where is req.variable stored?

Similar question (but NOT a duplicate): How do i store request-level variables in node.js?
Consider this following code:
const express = require('express');
const app = express();
app.get('/', (req, res) => {
req.someVariable = "Hello";
res.send(req.someVariable + ' world!');
});
Where is req.someVariable stored? In a cookie? Does this apply for the user only or everyone? Also, what's the difference between these and storing them in sessions/cookies?
In addition to req being just a javascript object variable, stored in memory, do note express has a res.locals object variable that persist through the request/response cycle.
If you want to store any user information, you should be using the res.locals object so you do not accidentally overwrite other important objects.
res.locals
An object that contains response local variables scoped to the
request, and therefore available only to the view(s) rendered during
that request / response cycle (if any). Otherwise, this property is
identical to app.locals.
This property is useful for exposing request-level information such as the request path name, authenticated user, user settings, and
so on.
http://expressjs.com/en/5x/api.html#res.locals
app.use(function (req, res, next) {
res.locals.user = req.user
res.locals.authenticated = !req.user.anonymous
next()
})
Note: res.locals on it's own is not sent together with the response (e.g. res.send). It's only accessible in your nodejs app.
Cookies
Cookies are information bits that you want to send to the client's browser (stored in browser memory). The client can then send the cookie back to your nodejs app. These are different from req and res properties.
Cookie can store for example, an authentication token, which can be stored in the client's browser, and provided to the nodejs app on every request.
For security, you can use a httpOnly cookie which cannot be modified by browser javascript.

Are the variables stored in the Node.JS Express global namespace private to the current request?

Node.JS Express apps have a global object that acts as a "bag" you can attach data/objects to that is available to all the code in your application:
// Create an appRoot global variable that tells the code base what the root directory of this app is.
global.appRoot = path.resolve(__dirname);
My main concern is that I need to be sure that the global object is only global in the sense that all the code that operates in the context of the current client request an other has access the data in that object, and that the data is not available between or across requests. Otherwise I could be leaking data that must remain private to a particular request or user.
To make sure I'm being crystal clear, let's take an example scenario:
GET request #1: The name "john doe" is stored in global.userName
GET request #2: At the start of the request, global.UserName is unassigned and absolutely does not contain "john doe"
In other words, is the global object reinitialized with every single request or does it persist as long as the server is operational, available to all requests that reach the server and there aren't any "tricks" that could lead to unwanted data leakage across server requests?
The node.js global object is global to the entire node.js process. It doesn't have anything to do with Express. However, Express does have a few objects with different scopes that you can use for persisting data across a client request, or event across all client requests. From the Express docs:
res.locals
An object that contains response local variables scoped to the request, and therefore available only to the view(s) rendered during that request / response cycle (if any).
app.use(function(req, res, next){
res.locals.user = req.user;
res.locals.authenticated = ! req.user.anonymous;
next();
});
app.locals
The app.locals object has properties that are local variables within the application... Once set, the value of app.locals properties persist throughout the life of the application, in contrast with res.locals properties that are valid only for the lifetime of the request.
The Express app.locals object is similar to the node.js global object insofar as it being maintained in memory across multiple requests. However, it's local to an instance of Express, and should that instance be deleted, so will the app.locals object. The node.js global object differs in that it will stick around for the lifecycle of the entire node.js process.
Yes, global variables will persist for any request. Unless you spin another instance of your node application. If you're looking to some authentication/session tools there are quite a lot out there, like passport or cookie-parser

Asynchronous function in express middleware

I use express 4+ and i have a registration route that made and asynchroneous call before render the page. the code is below:
router.get('/registration' , (req , res)=>{
let query = new Parse.Query(Parse.Role);
query.equalTo('public',true);
query.find().then((rs)=>{
let roles = rs.map((r)=>{
return {'id':r.id , 'name':r.get('ui_name')}
});
res.render('registration/registration.njk' , {roles:roles});
} , (error , code)=>{
res.send(new Error(500));
})
})
The problems is when i do that, all the middlewares are called twice and all sessions data and cookie is removed. This cause problems because i can't fetch current user.
How can i prevent this ?
You need to use persistent session to complete your work flow, looks like due to non-persistent session your previous session data getting over-written. Please refer to the options your session middleware provides one of the option would be helpful to make it store the session you're already using.
If possible please mention which session middleware you're using.

How to persist additional data added on fly in Passport sessions

I have a simple authentication system built on Passport.js on top of Node.js. I have a use case where I need to persist Organisation ID in the session which is to be updated on hitting of a particular route.
I did this using the middleware:
app.use('/switchOrganization',function(req, res, next) {
if(req.user) req.session.passport.user.activeOrg = 'my org';
next();
});
But this doesn't persist the data in other routes:
app.route('/someRoute').post(function(req,res){
console.log(req.session.passport.user);
});
It doesn't contains the 'activeOrg' value. Where I am going wrong?
The user object is deserialized into req.user in the deserializeUser function. This happens on each request. So if you make change to req.user in one of the route handlers, and expect it to be persisted for the route handlers that follow it, it'll only be the case for that particular request. If /someRoute is requested anew, it will not have the the changes you made in another route which wasn't invoked in the new request.
You should instead persist the changes to database so that when the deserializeUser function is called for the next request, it'll have your data from the beginning.
I am using redis sessionStore. And if I write to req.session, then data persists on redis store:
req.session.access_token = result.access_token;
req.session.refresh_token = result.refresh_token;
req.session.instance_url = result.instance_url;
Redis Screenshot

How to avoid sessions until logged in within Express

I cannot seem to figure a way to prevent Express/Connect to create sessions until I have a valid log in from the user.
The problem especially arises when using a DB-Backed Session Storage and calling the REST Services from non-browsers as in such cases, the Connect Session Object will create a new Session for each request which I do of course want to prevent.
However, I do need sessions whenever the user is authenticated as I am using Passport.js for authentication which requires sessions as well as I do require it to load session data from sent cookie information.
Looking at the source of the Connect Session Code, it seems it is always creating a new Session if none got sent from client without any option to prevent it..?
thanks
Alex
If you can easily identify calls to your API at query time you could do something like this:
app.use(function(req, res, next){
if ( req.path.indexOf("/api") == 0 ) return next();
else return express.session()( req, res, next );
});
This way the session middleware is only included if the request URL doesn't match some condition. I haven't tried this in anger though, so you might want to consider initialising express.session() outside the function, and make sure there aren't any other repercussions.

Resources