Node/Express - User/session variables - node.js

I am attempting to prototype a user journey in Node. The prototype will be used by several testers in parallel.
How can I define and store variables via the routing file that are only accessible by the user on that session?
e.g.
router.post('/thepage', (req, res) => {
passedInfo = req.body
dataObj.incomeWork = passedInfo['work']
dataObj.incomeRent = passedInfo['rent']
dataObj.incomeOther = passedInfo['other-income']
res.render(`/thepage`, dataObj )
}
I've looked a the express-session module, but that requires the variable to be passed from route to route to sustain its values.

Related

Keeping track of users history in express to make a 'back' button?

I'm making an express app and I want to include a 'back' button in the app UI that does basically exactly what the browser back button does.
I tried holding an array variable in the server that simply collects all of the URL params visited. For example, for the '/:lang' route ...
const browsingHistory = [];
app.get("/:lang", (req, res) => {
const lang = req.params.lang;
if (lang === "en" || lang === "fr") {
const templateVars = {
menuItems: db[lang].menuItems,
lang,
};
res.render("root", templateVars);
}
if (lang !== "favicon.ico") {
browsingHistory.push(lang);
console.log(`Browsing history: ${browsingHistory}`);
}
});
BUT I'm realizing this only works when locally hosted — once deployed, if there are multiple users simultaneously, how to keep track of each users' individual history? Or is there a better way of doing this?
Storing the browsing history will require user sessions. On each request, you will have to store the route that the user hits in their session variable.
In Express, this can be accomplished with the express-session library. You will want to initiate each session with some history property that begins as an empty array. Once express-session is set up, you can do something similar to the following
app.get("/:lang", (req, res) => {
const lang = req.params.lang;
req.session.history.push(lang);
...
});
app.get("/getMyPageHistory", (req, res) => {
res.send(req.session.history);
});
req.session will be unique for each user. So, you can store each user's unique history in this variable.
With that said, if you go down this route, you eventually will want some external session storage. By default, sessions are saved in your server's memory. This introduces a few issues that are explained in the express-session documentation. Here is their warning
Warning The default server-side session storage, MemoryStore, is purposely not designed for a production environment. It will leak memory under most conditions, does not scale past a single process, and is meant for debugging and developing.
They provide a list of compatible session stores

How to properly use dataloaders across multiple users?

In caching per request the following example is given that shows how to use dataloaders in express.
function createLoaders(authToken) {
return {
users: new DataLoader(ids => genUsers(authToken, ids)),
}
}
var app = express()
app.get('/', function(req, res) {
var authToken = authenticateUser(req)
var loaders = createLoaders(authToken)
res.send(renderPage(req, loaders))
})
app.listen()
I'm confused about passing authToken to genUsers batch function. How should a batch function be composed to use authToken and to return each user corresponding results??
What the example is saying that genUsers should use the credentials of the current request's user (identified by their auth token) to ensure they can only fetch data that they're allowed to see. Essentially, the loader gets initialised at the start of the request, and then discarded at the end, and never recycled between requests.

Instantiate node module differently per (web) user

I was wondering what the best practice is for the following scenario:
I am planning to use an npm module for a web servie, where the user enters a access and secret key. Then a module is used which is instantiated like this:
var module = require('module')('ACCESS_KEY','SECRET_KEY');
Each user of course has a different access and secret key. The module exposes several functions which I want to use with the user's access and secret key on his behalf.
Now my question is, how I can 'require' that module with the keys from the database for each user, not just for the whole application with a single static pair. I am on node 8 and using ES6.
The crucial detail here is that this:
var module = require('module')('ACCESS_KEY','SECRET_KEY');
...is equivalent to this:
var moduleFunc = require('module');
var module = moduleFunc('ACCESS_KEY', 'SECRET_KEY');
In other words, 'module' exports a function, and you're calling that function with two arguments ('ACCESS_KEY', 'SECRET_KEY') and assigning the result to module.
That means you can instead require('module') at the top of your file and then use the function it gives you as many times as you want later on, with different arguments.
For example:
const someApi = require('some-api');
// ...later...
app.get('/', (req, res) => {
const { ACCESS_KEY, SECRET_KEY } = getUserKeys(req);
const apiClient = someApi(ACCESS_KEY, SECRET_KEY);
// ...
});

Explain to Mean.io beginner how Mean.io sample package's authentication works

I'm learning mean.io from this tutorial video, which shows the example package (created by mean package mymodule. It is also described under "Packages" on the docs). I would like help in understanding how the given authentication/authorization works.
The default sample package/module has a simple user authentication that on the client side
myapp/packages/mymodule/public/views/index.html contains:
<li>
Server route that anyone can access
</li>
<li>
Server route that requires authentication
</li>
<li>
Server route that requires admin user
</li>
On the server side,
myapp/packages/mymodule/server/routes/mymodule.js, contains:
// The Package is past automatically as first parameter
module.exports = function(Mymodule, app, auth, database) {
app.get('/mymodule/example/anyone', function(req, res, next) {
res.send('Anyone can access this');
});
app.get('/mymodule/example/auth', auth.requiresLogin, function(req, res, next) {
res.send('Only authenticated users can access this');
});
app.get('/mymodule/example/admin', auth.requiresAdmin, function(req, res, next) {
res.send('Only users with Admin role can access this');
});
...
};
The magic of the different authentication relies on the second argument of app.get() with additional authentication callback: none, auth.requiresLogin, or auth.requiresAdmin.
This is the authentication magic (also on github):
myapp/packages/access/server/config/authorization.js:
/**
* Generic require login routing middleware
*/
exports.requiresLogin = function(req, res, next) {
if (!req.isAuthenticated()) {
return res.send(401, 'User is not authorized');
}
next();
};
/**
* Generic require Admin routing middleware
* Basic Role checking - future release with full permission system
*/
exports.requiresAdmin = function(req, res, next) {
if (!req.isAuthenticated() || !req.user.hasRole('admin')) {
return res.send(401, 'User is not authorized');
}
next();
};
QUESTION A: Why is it "exports.requiresLogin" and "exports.requiresAdmin" in the authorization.js instead of "somethingelse.requiresLogin" and "somethingelse.requiresAdmin"? Is this "exports" related to the myapp/packages/access/server/config/passport.js's exports: module.exports = function(passport) { ...}, github? If so, in what circumstances can we use this "exports"?
Since authentication's authorization rules is written up in package "access" and used in package "mymodule", Mean.io packages are not independent of each other. The Access package is registered on
myapp/packages/access/app.js, github:
var mean = require('meanio'),
Module = mean.Module,
passport = require('passport');
var Access = new Module('access');
Access.register(function(database) {
// Register auth dependency
var auth = require('./server/config/authorization');
require('./server/config/passport')(passport);
// This is for backwards compatibility
mean.register('auth', function() {
return auth;
});
mean.register('passport', function() {
return passport;
});
Access.passport = passport;
Access.middleware = auth;
return Access;
});
QUESTION B: Does Mean.io automatically link all the packages or is there code to link packages somewhere? Is it linked due to the part with "This is for backwards compatibility" shown below? If so, where can "auth" be used? All the packages myapp/packages/? How about in the mean.io base app directory myapp/?
var auth = require('./server/config/authorization');
// This is for backwards compatibility
mean.register('auth', function() {
return auth;
});
QUESTION C: Why is it "Access.passport = passport;", but "middleware" for "Access.middleware = auth;"? What what happen if it were "Access.auth = auth"?
REGARDING QUESTION A (on the use of exports)
In Node.js, assigning values to the exports object makes those values available to the code that requires the source file.
For example, given file foo.js:
exports.foo = "FOO";
exports.bar = "BAR";
and file main.js:
var foo = require('foo.js');
console.log('foo=',foo.foo,'; bar=',foo.bar);
running node main.js will output foo= FOO ; bar= BAR.
See, for example, Node's module documentation or this write-up on require and exports.
REGARDING QUESTION B (on package "linking")
The answer to this question is the complement to the answer to Question A.
There is code to "link" the packages. It is the require statement.
In your app.js source code, the first line (reading var mean = require('meanio')) will set the local variable mean to whatever values are assigned to exports object is when meanio.js and/or the meanio module is loaded.
Same with passport = require('passport'). In that case, the local variable passport will be equal to the value of exports after index.js in the passport module is loaded.
REGARDING QUESTION C
I'm not entirely sure what you are asking here, but let me take a stab at it.
In this case:
1) var mean = require('meanio') in line 1 "imports" the meanio module, such that the local variable mean is more or less set equal to the value of exports in the meanio module.
2) Module = mean.Module in line 2 sets the local variable Module equal to the value of mean.Module, which must have been assigned in the meanio module.
3) var Access = new Module('access') is instantiating an instance of the Module class, assigning it to the local variable Access.
4) Access.passport = passport assigns the instance variable named passport within the instance of meanio.Module named Access (to the value of the passport module required on line #3)
5) Access.middleware = auth assigns the instance variable named middleward within the instance of meanio.Module named Access (to the value returned by require('./server/config/authorization') in line 11).
I'm not familiar with the "meanio" module, but based on this code it looks like you are configuring the meanio.Module("access") instance (named Access) by assigning specific "magic" variable names.
In other words, rather than Access.passport = passport; Access.middleware = auth you might have Access.setPassport(passport); Access.setMiddleware(auth) or (rather than line 5) var Access = new Module('access',passport,auth).
That is, the author of the "meanio" module seems to have decided to use special variable names to configure the class rather than "setter" methods or parameters passed to the constructor. I assume that somewhere in the meanio code you'll find a reference to something like this.middleware and this.passport, where the code is assuming you have "filled in" those instance variables as happens in the last few lines in your code sample.
If you were to add Access.auth = auth then all that would happen is that the Access object would have a new attributed named auth whose value is equal to the that of the local variable auth.
If you used Access.auth instead of Access.middleware, I assume whatever code in the Access class that is using this.middleware will fail, since no value was ever assigned to Access.middleware and Access.auth is not one of the "magic" variable names that meanio is looking for.

How do i store request-level variables in node.js?

for data that only needs to be available during an individual request, where should it be stored?
i am creating new properties on the req and res objects so i dont have to pass that data from function to function.
req.myNewValue = 'just for this request'
is the process object an option? or is it shared globally across all requests?
In Express 4, the best practice is to store request level variables on 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.
app.use(function(req, res, next){
res.locals.user = req.user;
res.locals.authenticated = ! req.user.anonymous;
next();
});
The process object is shared by all requests and should not be used per request.
If you are talking about the variable passed like here:
http.createServer(function (req, res) {
req.myNewValue = 'just for this request';
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
then it is perfectly fine what you are doing. req stores the request data, you can modify it as you want. If you are using some framework like Express, then it should be fine as well (keep in mind that you may overwrite some built-in properties of req object).
If by "process object" you are refering to the global variable process, then absolutely not. The data here is global and shouldn't be modified at all.
If you want to preserve the data across the async callback and there could be scenarios, where request and response objects are not available. So in that case continuation-local-storage package, is helpful.
It is used to access the data or the current express request/response from a point where that is not readily accessible. It use the concept of namespace.
Here is how I set up this
Install the continuation-local-storage package
npm install continuation-local-storage --save
Create namespace
let app = express();
let cls = require('continuation-local-storage');
let namespace = cls.createNamespace('com.domain');
then middleware
app.use((req, res, next) => {
var namespace = cls.getNamespace('com.domain');
// wrap the events from request and response
namespace.bindEmitter(req);
namespace.bindEmitter(res);
// run following middleware in the scope of the namespace we created
namespace.run(function () {
// set data on the namespace, makes it available for all continuations
namespace.set('data', "any_data");
next();
});
})
Now in any file or function you can get this namespace and use the saved data in it
//logger.ts
var getNamespace = require("continuation-local-storage").getNamespace;
let namespace = getNamespace("com.domain");
let data = namespace.get("data");
console.log("data : ", data);
No, it isn't shared along with all requests, it only persists for that request long.

Resources