I am a web developer with a PHP background that moved recently to JS.
In PHP, we used to have the $_SESSION global variable which make your session variables accessible from any 'place' in your code (Great feature!).
Now, working with an ExpressJS application, I am using express-session package to create session variables.
The issue is that session variables in ExpressJS are NOT global, they are property of the request object (req.session). So they are only accessible in functions that have a req parameter (AKA middlewares and route functions).
The question is: Is it possible to make session variables in ExpressJS global ala PHP, so any 'helper' function can handle them?
Yes, but you should not do that. You can create globals by appending properties to the global object. For example: global.someData = req.session.someData, but the global object is similar to the GLOBAL from PHP.
In that case, especially if you have async functions, the variables of one request will mess up with the code running for another request (e.g. Alice would get the Bob's variables).
The good practice is to pass the req.session object wherever that is needed and access it.
You can use CLS (Continuation-local-storage) to achieve this.
All you have to do is create a namespace and bind it to each request.
Now, add the below code to your server.js where you initialize your server
//server.js
var express = require('express');
var app = express();
var createNameSpace = require('continuation-local-storage').createNamespace;
var myNameSpace = createNameSpace('myNameSpace');
// Assign your sessionVariable to each request as 'sessionId'
app.use(function(req, res, next) {
myNameSpace.run(function() {
myNameSpace.set('sessionId', sessionVariable);
next();
});
});
Add the following code anywhere in you application to access your sessionVariable
var getNamespace = require('continuation-local-storage').getNamespace;
var myNameSpace = getNamespace('myNameSpace');
var sessionId = myNameSpace.get('sessionId'); // access your sessionId (sessionVariable)
Note that using CLS can be tricky sometimes.
Once your session variable is setup (either on your login function or wherever you are setting it up) it is then a global variable on your index.js file.
I setup my session like so:
var students = {Id: result[key].Id,Username: result[key].Username,Email: result[key].Email,Phone: result[key].Phone,First_Name: result[key].First_Name,Surname: result[key].Surname,RoleName: result[key].RoleName, Company: result[key].CompanyName };
Users.push(students);
req.session.user = students;
You can access the stored session by setting up a var like the below:
var session = req.session.user
This will set the session to a session variable where you need it inside your function.
Then when you render a page you just need to add the session to that render and you will have access to it on that page.
res.render('/', { title: 'Hello World', session: session});
Related
I'm using Hapi.
I'm also using NES client to connect to another node instance. In order to access this socket from the routes, I attach it to the server variable like so:
exports = async () => {
//api
var server = new Hapi.Server({
host: process.env.WEB_HOST || '127.0.0.1',
port: process.env.WEB_PORT || '8080'
});
// register plugins
await server.register(plugins);
// add routes
await routes(server);
server.socket = new Socket(identifier); // uses NES
return server;
}
I want to access the socket from a library file. How do I do this without having to pass it the server variable each time? When I try to module.exports the server, it never gives me a reference, only a version of the variable at the time it was exported.
Have you tried server.decorate. If I understood correctly, you want to access your socket variable from routes. Why not just create basic plugin and create your socket instance and pass that into server object. For example;
exports.plugin = {
async register(server, options) {
server.decorate('server', 'socketConn', new Socket(identifier));
},
name: 'socket-connection'
};
and in your routes you can access this variable as request.server.socketConn.
If I use express-session the session variable becomes available under req.session in for example:
app.get('/', function(req, res) {
req.session.myVar = 1;
}
But what if I want to retreive the session of the current request deeply nested in my application where I do not have the req variable available?
Is there another way besides passing in the req variable as a parameter all across the framework?
Is there another way besides passing in the req variable as a parameter all across the framework?
No, not really. A node.js server (that uses any asynchronous operations) can have multiple requests in flight at the same time. So, any request-specific data that you want to access has to come from an object that is associated with this particular request and only this specific request. You can't put it in globals because those can be intermixed from different requests. You have several options, but ultimately you have to pass the data through your functions to wherever it is needed -there is no shortcut here. Here are several options:
Put the data on req and pass req through your code to the function that needs the data.
Pass the data itself (no need to pass the whole req object if you only need once piece of data.
Create a new object that is specific to this particular request (not shared with other requests or available to other requests) and put the desired data as a property on that object and then pass that object through to the desired code. In an OO world, you can usually put multiple functions as methods on a shared object and then the data is automatically available to all those methods so you don't have to explicitly pass it.
Use a shared scope and closure so that any functions that need access to the data can get it directly from a parent scope.
The solution for me was to use Continuation-local-storage as middleware for express like outlined in this question as well NodeJS TransactionID with Continuation-local-storage
import * as cls from "continuation-local-storage";
cls.createNamespace('mynamespace');
app.use((req, res, next) => {
let session = cls.getNamespace('mynamespace');
session.bindEmitter(req);
session.bindEmitter(res);
session.run(function() {
session.set('req', req);
next();
});
});
and when you need it later on:
var session = cls.getNamespace('mynamespace');
var req = session.get('req');
I've built a very simple translation module for Express JS, it's a global object in the application scope that's instantiated at application runtime:
translator.configure({
translations: 'translations.json'
});
I've added some simple middleware to Express JS that changes the locale in the translator module for each request:
app.use(function(req, res, next) {
var locale = // Get locale from request host header
// Setup the translator
translator.setLocale(locale);
// Attach translator to request parameters
res.locals.__ = translator.translations;
// Pass control to the next middleware function
next();
});
Then I access my translations through the variable __ in my views (here I use ejs):
...
Here is my translated text: <%= __['test'] %>
...
My translator module looks like this:
var translations,
locale;
// public exports
var translator = exports;
translator.configure = function(opt) {
translations = require('./' + opt.translations);
};
translator.setLocale = function(locale) {
translator.translations = translations[locale];
}
The translations.json file is just a simple JSON structure:
{
"us":{
"test": "Hello!"
},
"es":{
"test": "Hola!"
}
}
My question is, is this overall structure a bad idea? I do not have extensive knowledge about express JS. The global object makes me kind of nervous since the translations are based of its current state, which is changed from request to request, any problems here? Does express JS fully complete a request before handling the next one, or is there some level of concurrency going on that can mess up my translations?
A global object is a bad idea for saving state that is used during a request. A request is not necessarily completed before the next one starts running. If, at any time, a request handler makes an asynchronous call (like to read a file), then another request can start running at that point.
In general, you should store state related to a specific request on the request object itself. That way, it is not global and is stored specifically for that request only and you can have as many requests going at once without conflict.
So, you could ideally not store any request-specific state in your translator object at all unless you create a new translator object for each request and then store that specific translator object in the request object.
I don't follow your translator code exactly, but this looks like trouble:
app.use(function(req, res, next) {
var locale = // Get locale from request host header
// Setup the translator
translator.setLocale(locale);
// Attach translator to request parameters
res.locals.__ = translator.translations;
// Pass control to the next middleware function
next();
});
Because it looks like you're configuring a shared, global translator object, then expecting to use it later and expecting it not to be changed by any other request. That seems like asking for trouble.
If your request handler makes any async call at any point, then another request handler can run which can create a conflict as both try to use the same translator object.
I just hit an area of confusion thinking about the way that node is serving each user..
From my limited understanding, with PHP,a new (thread?) environment was created for each user, so my script only needed to focus on one individual user and that would apply to each individual.
Now, from my very limited understanding of node, one event loop is handling all of my users. So in this script snippet for example,
//imaginary script to validate user's post to server (log-in attempt) next:
console.log('user logged in successfully!');
var username = req.body.username;
//imaginary script that takes the username and processes it somehow, using my username variable
If another user has logged in since the last user, the username variable will have been overwritten, and now everything is messed up. Whereas with PHP, when a new user connected, he'd be handled with a new, reset environment..
That may not be exactly right, but what I think I'm seeing is that I need to keep ALL of the user-associated data on the client side, and pass everything between the client and server as a package, not keeping any user data stored server-side.
Am I on track here, completely confused, or is there a better way to think about this?
It all depends on the scope of the variable (where it was created). Consider this simple and simplified example:
var http = require('http');
var variableOne = "foobar";
http.createServer(function (req, res) {
var variableTwo = "barfoo";
}).listen(1337, '127.0.0.1');
variableOne is sort of global, since the node.js will go though the declaration of the variable only once during start up time. The variable is available everywhere in the scipt so it could be changed by every request/user.
variableTwo is created for every request that node.js handles. It is possible, that there are multiple versions of this variable.
When handling permant user data you need to store it some way it is accessible on a later request but unique for the user.
Again a simplified example:
var http = require('http');
var userData = {};
http.createServer(function (req, res) {
userData[request.connection.remoteAddress] = "some unique user data";
}).listen(1337, '127.0.0.1');
It will save some unique user data per IP address. You can read the data later by checking if the IP address was used before. This is not very practical but should give you an idea. In a real world example you would generate an unique cookie per user.
I'm currently writing a server-centric package for Meteor, and the relevant code looks something like this:
__meteor_bootstrap__.app.stack.unshift({
route: route_final,
handle: function (req,res, next) {
res.writeHead(200, {'Content-Type': 'text/json'});
res.end("Print current user here");
return;
}.future ()
});
This is obviously a relatively hacky way of doing things, but I need to create a RESTful API.
How can I access Meteor.userId() from here? The docs say it can only be accessed from inside a method or publish. Is there any way around that?
Things I've tried:
Capture it from a publish using Meteor.publish("user", function() { user = this.userId() });
Get the token + user id from the cookies and authenticate it myself using something like Meteor.users.findOne({_id:userId,"services.resume.loginTokens.token":logintoken});
Create a method called get_user_id and call it from inside my code below.
The thing that you need to target first is that to get something that can identify the user from headers (especially because you want to get the username at a point where no javascript can run).
Meteor stores session data for logins in localStorage, which can only be accessed via javascript. So it can't check who is logged in until the page has loaded and the headers have been passed.
To do this you need to also store the user data as a cookie as well as on localStorage:
client side js - using cookie setCookie and getCookie functions from w3schools.com
Deps.autorun(function() {
if(Accounts.loginServicesConfigured() && Meteor.userId()) {
setCookie("meteor_userid",Meteor.userId(),30);
setCookie("meteor_logintoken",localStorage.getItem("Meteor.loginToken"),30);
}
});
server side route
handle: function (req,res, next) {
//Parse cookies using get_cookies function from : http://stackoverflow.com/questions/3393854/get-and-set-a-single-cookie-with-node-js-http-server
var userId = get_cookies(req)['meteor_usserid'];
var loginToken = get_cookies(req)['meteor_logintoken'];
var user = Meteor.users.findOne({_id:userId, "services.resume.loginTokens.token":loginToken});
var loggedInUser = (user)?user.username : "Not logged in";
res.writeHead(200, {'Content-Type': 'text/json'});
res.end("Print current user here - " + loggedInUser)
return;
}.future ()
The cookie allows the server to check who is logged in before the page is rendered. It is set as soon as the user is logged in, reactively using Deps.autorun
My solution was inspired by the server part of #Akshat's method. Since I'm making a RESTful API, I just pass the userId/loginToken in every time (either as a param, cookie or header).
For anyone interested, I bundled it as a package: https://github.com/gkoberger/meteor-reststop