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.
Related
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.
I'm reading the Express framework docs, making my basic login/redirect page routes.
The following route accepts the submission:
app.post('/',function(req,res){
//console.log("USERNAME: "+req.body.username);
//console.log("PASSWORD: "+req.body.password);
res.redirect('/chat');
});
and this:
app.get('/chat', function(req, res){
res.sendFile(__dirname + '/templates/chat.html');
//console.log("request");
});
takes the user to a new page.
How do I send context? Should I be using res.render()? Neither function seems to contain an option for data like {username:req.body.username}. How should data be passed between routes?
Generally to handle logins with express you'd use something like passport's local strategy, which attaches a user object to the request object (req.user) for you for each route. I don't know that what you're trying will work in a larger context -- you'd need some kind of session-based middleware like express-session at the very least, so you can attach variables per session (I think it gives you req.session). By default, express has the capability to store information for one request/response cycle (res.locals) or for the entire instance of the app (i.e. for all users) (app.locals).
As far as getting data into views, you would use res.render with something like EJS, pug, or another view engine. For example, if in your route, you had something like:
route.get('/', (req, res) => {
res.render('template', { username: 'yourname' })
}
you can refer to that in your ejs template like so:
<h1>Hello, <%= username %>!</h1>
which will get sent back as this:
<h1>Hello, yourname!</h1>
So, to answer your question:
You would use res.render to get variables & data into your views
You don't share data across routes by default except app-level data that applies to all users, which can be set on app.locals
You can use authentication middleware like passport and session middleware like express-session to keep track of user information across routes per session.
Hope this helps! Good luck with express!
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
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.
I'm rather new to node, I have started writing a small app and am working on my user auth and profile. I'm using express, passport and ejs. I've got passport working and have my user redirecting to a profile page that requires additional data before the user begin's to use the application. My question is:
What's the proper way to update the user's session (created at passport.authentication) when a user updates their profile data? I would like to update the user session in order to hit it rather than the data base for basic user data.
If your using express you have middleware that looks like:
function (res, req, next){}
When you get session data you can add it to the req object.
function (res, req, next){
// get the session here...
req.session = session;
}
Then any other middleware will have access to the req.session object which will contain the user's session.