Global variable across all controllers in Node JS - node.js

I am trying to have a variable which can be accessible by all controllers in my node project. Currently in one controller I have:
var ua = req.headers['user-agent'];
var isMobile = "no";
if(/mobile/i.test(ua))
isMobile="yes";
It's pointless to copy past all of this for all my controllers and pass the isMobile variable to the view. I'd like to get the value of isMobile set once, and then pass it wherever I want from my controllers.
Is there an easy way to do this rather than have those 4 lines of code copy pasted in every controller?
Thanks

You'll want to use a Sails policy for this:
// /api/policies/isMobile.js
module.exports = function(req, res, next) {
var ua = req.headers['user-agent'];
req.isMobile = /mobile/i.test(ua);
next();
}
// /config/policies.js
module.exports.policies = {
'*': 'isMobile'
};
This will run the code before every controller action, and give you access to the req.isMobile var in all of your custom controller code.

A truly global variable isn't particularly an option as any concurrency above 1 will likely result in unexpected behavior. Being that it is something particular to the unique request itself, the req object is likely your best bet.
Assuming you have access to the req object everywhere that you would like to utilize use this flag, you can simply add a property to the req object at any point (preferably early in the request/response cycle). After this property is added, it should be available everywhere that has access to req.
req.isMobile = /mobile/i.test(req.headers['user-agent']) ? 'yes' : 'no';
Or if there is a concept like middleware in express for sails
function isMobile(req, res, next) {
req.isMobile = /mobile/i.test(req.headers['user-agent']) ? 'yes' : 'no';
next();
}

Related

How will I pass values from router to controller in nodejs

I want to pass app.locals values into a controller function. How will I pass the variable inside the controller.
var enroll_controller=require('../enrollController');
router.post('/enroll',function(req,res){
var contractAddress=req.app.locals.contractAddress;
enroll_controller.newUser
});
I want to pass contractAddress to enroll_controller?
Anybody?
You can try this too if you don't want to use middleware.
var enroll_controller=require('../enrollController');
router.post('/enroll',function(req,res){
enroll_controller.newUser(req,res);
});
enrollCntroller.js
exports.newuser=function(req,res){
var contractAddress=req.app.locals.contractAddress
}
pass it as middleware:
router.post('/enroll',enroll_controller.newUser);
And in your controller function you can directly use it like this:
exports.newUser=function(req,res){
var contractAddress=req.app.locals.contractAddress
}
You can write a global middleware and set these values in that middleware if you want it to be available for every route.
// in your app.js file or main file which is wiring up application
app.use(function(req,res,next){
req.app.locals.contractAddress = "Set address here."
next();
});
var enroll_controller=require('../enrollController');
router.post('/enroll',enroll_controller.newUser);
or you can export run the above middleware to only one route and set address for that specific route.
var enroll_controller=require('../enrollController');
router.post('/enroll',
function(req,res, next){
// middleware running to one route only.
req.app.locals.contractAddress = 'set address here';
next();
},
enroll_controller.newUser
);
You can also export this middleware from a file and then reuse it instead of defining for every time as anonymous function.

Node express api routes for multilingual directory like url

Does any one knows an example or could explain here how node.js and express would have to route for a multilanguage site? I'm using i18n-node for translation and folder like routing ( /es/, /de/ , etc ) for different languages. This all are static routes but I also have routes like apiRoutes.route('/user/profile') using 'app' at the begining ( app.get('/app/user/profile') so please consider this in your answer so is NOT necesary route to : app.get('/es/app/user/profile') .
having 15 routes like this now:
app.get('/terms', function(req, res) {
res.render('terms',{
...
});
});
how it have to be set for routes like:
app.get('/es/terms', function(req, res) {
res.render('terms',{
...
});
});
Should I duplicate this routes and add for example a locale for
each like:
app.get('/es/terms', function(req, res) {
res.render('terms',{
...
});
});
Or Should do something like:
if cookie['lang'] && cookie['lang'] is in locales
// then redirect to /:lang/terms
else
// show default language in /terms
if req.headers["accept-language"] && req.headers["accept-language"]
// then redirect to /:lang/terms
else
//show default language in /terms
Or there is another way I should approach this that follows good practices or is better respecting standards?
Miro's Answer in :
How can I get the browser language in node.js (express.js)? says I should use app.all('*', ...
Is this all I need?, ..still, it might have a syntax error or i'm not understanding well this two parts
var rxLocal = /^\/(de|en)/i;
...
app.get(/\/(de|en)\/login/i, routes.login);
thanks in advance
You need to consider 2 things :
1. How get the local :
Accept-Language
The HTTP protocole define the Accept-Language header to manage the local. This is a normalized method. You can access it with the req.acceptsLanguages method of express.
+Normalized
+Natively support by brower
-Not easy to by passe by the end user
Path / Cookies
You can get the local from the path. In express it can be do with a parameter patter like /:local/rest/of/path and retrieve in the request object with the req.param method.
You can also get the information from the cookies with the req.cookies properties (don't forgot to set it).
Both
To increase the user experience you can mix the both method. For exemple get the default language from the HTTP header send by the browser but permite to the user to override this in you application and store this parameter in the cookies.
2. Use the local:
Each methods to get the local can be used from different way. I will
use random of them in exemple but they are all compatible.
Top level configuration.
In case of you use a template Engine and you controller can be local agnostic. You can use a middleware to get the local information and configure the render engine.
app.use('/:local' (req, res, next) => {
let localKey = req.param('local');
res.locals = // Some ingenious method to get the locales from localKey
next();
}
Check res.locals and your engine documentation.
Use it in controller.
If the local is part of the contoller process. You can get directly is value in controller.
In case of you use a complexe method to determine the final value of the local, you can also use a middleware to determine this value and enrich the request with it.
app.use((req, res, next) => {
let local = req.cookies.local;
if(!local) local = req.acceptsLanguages();
if(!local) local = 'en-US';
req.local = local;
}
Both
You can use both method too. It depend of what you need. Find the best way to get a maintainable code and avoid replication for your use case.
When you use middle where witch impact the controllers, be sure you declare them before your routes.
You can use a route parameter to get the locale from the URL, like this:
app.get('/:lang/terms', function (req, res) {
if (req.params === 'es') {
res.send('¡Hola!');
else {
res.send('Hi!');
}
});
The colon character tells Express to put whatever is between the first to slashes of the path in req.params.lang.
See express routing documentation for details.

Express.js unique var per request outside routing

In my express application I have a module called helpers thats is required in almost all my routes and modules. This module has a logger method that logs to fluentd (but that's unimportant). While building the data to log I'd like to add a unique identifier of the request, so that all the logs written for the same request have the same unique ID. Using a global var in the app entry point app.use doesn't work because this var would be overwritten every time a new request hits, so the global uuid will change would obviously change in case of high load or long running tasks. The res.locals is not available outside routing, so I can't use it for this matter. Is there a way to create a var that would be unique per request and available in every module or maybe a way to access the res.locals data outside routing? Thank you
EDIT
Maybe an example will help understand better the question.
Suppose I have a module called helpers.js like this:
let helpers = {};
helpers.log = (logData, logName) => {
fluentLogger.emit('', {
name: logName,
//uuid: the needed uuid,
message: logData
});
}
module.exports = helpers;
Now obviously I can do this in my app.js entry point:
app.use(function (req, res, next) {
res.locals.uuid = uuid.v4();
next();
});
and then in every loaded middleware module that requires helpers(adding a new param to the helpers.log method):
const helpers = require('helpers');
router.post('/', (req, res, next) => {
helpers.log('my log message', 'myLogName', res.locals.uuid);
next();
});
and this will normally work. But suppose a big or middle size project where there are hundreds of custom modules and models (not middlewares) and a module may require other modules that require other modules that require finally the helpers module. In this case I should pass the res.locals.uuid as a parameter to every method of every method so that I have it available in the logger method. Not a very good idea. Suppose I have a new module called dbmodel.js that is required in a middleware function:
const helpers = require('helpers');
let dbmodel = {};
dbmodel.getSomeData = (someParam) => {
//some logic
helpers.log('my log message', 'myLogName');
}
module.exports = dbmodel;
The dbmodel has no idea about the res.locals data if I don't pass it from the middleware, so the helpers.log method will also have no idea about this.
In PHP one would normally write a GLOBAL var in the application's entry point so a hypothetical logger function would have access to this global on every method request from whichever class of the application.
Hope this explanation will help :) Thank you
EDIT 2
The solution for this kind of problems is CLS. Thanks to #robertklep for the hint. A good slideshare explaining exactly the same problem (logger with unique ID) and explaining the CLS solutions can be found here: https://www.slideshare.net/isharabash/cls-and-asynclistener
I answered a very similar question here which will solve this problem.
I used to solve the problem the libraries node-uuid and continuation-local-storage. Take a look to the answer of this question and see if it helps:
NodeJS Express - Global Unique Request Id
And you want a bigger explanation, take a look here:
Express.js: Logging info with global unique request ID – Node.js
Yes you can do so by one method .
Every request comes to his routes pass that request inside the middleware.
Suppose you have
app.get('/', function(req, res) {
res.sendFile(path.join(public + "index.html"));
});
a request.
Place Middleware in it .and edit req field coming , in this way you will get the unique variable values for each request
check out this .
https://expressjs.com/en/guide/writing-middleware.html
Like this
var requestTime = function (req, res, next) {
req.requestTime = Date.now()
next()
}
app.use(requestTime)
app.get('/', function (req, res) {
var responseText = 'Hello World!<br>'
responseText += '<small>Requested at: ' + req.requestTime + '</small>'
res.send(responseText)
})
Here req.requestTime is unique for each request.

NodeJS + session object in ALL views without passing it on all controller actions

I want my session to be available in all views (*.ejs) without having to pass it on every single action. My code is shown below, but the req.session object is always null here, even though in my "controllers" I can access a session object after an user has authenticated, by specifying:
req.session.whatever
My initialization code (that is currently executed on every single request (I double checked with a debug breakpoint) is:
var appendLocalsToUseInViews = function(req, res, next)
{
//append request and session to use directly in views and avoid passing around needless stuff
res.locals.request = req;
if(req.session != null && req.session.user != null)
{
res.locals.user = req.session.user;
}
next(null, req, res);
};
I register this function in the app setup preamble:
app.use(appendLocalsToUseInViews);
I have seen people use app.use methods and dynamicHelpers. I am using express 3, and it seems they are gone, deprecated from Express 2... But that does not seem to be the point, as the function is being called correctly on every single request. How to I access the Express session in this sort of pre-controller code?
Thanks!
SOLUTION thanks Jani Hartikainen:
I moved the code to after the session middleware is loaded and its working!!! Here is the new code.
app.use(express.cookieParser(appSecret));
app.use(express.session({ secret: appSecret }));
---->>>app.use(appendLocalsToUseInViews);
This should work but make sure your app.use for this is only after you have initialized your session middleware. If you have this before the initialization for the session middleware, it will be ran before it in the chain, and thus the data will not be available.

Is it OK to add data to the response object in a middleware module in Express.js?

Here's the basic setup. I'm trying to create a simple middleware component that would allow me to easily pass data from my route directly to my javascript in the client side. (Very similiar to the Gon gem in ruby). The way I'm doing it is by having a module that looks like this:
module.exports = function(){
return function(req,res,next){
var app = req.app;
if(typeof(app) == 'undefined'){
var err = new Error("The JShare module requires express");
next(err);
return;
}
res.jshare = {};
app.dynamicHelpers({
includeJShare: function(req,res){
if(typeof(res.jshare) === 'undefined'){
return "";
}
return function(){
return '<script type="text/javascript">window.jshare=' + JSON.stringify(res.jshare) + '</script>';
}
}
});
next();
};
}
Then, in my route I can do this:
exports.index = function(req, res){
res.jshare.person = {firstName : "Alex"};
res.render('index', { title: 'Express' })
};
Finally in the layout.jade:
!{includeJShare()}
What that does is in outputs a line of javascript on the client that creates the exact JSON object that was created server side.
Here's the question; it all works as expected, but being new to Express and Node.js in general, I was just curious if attaching properties onto the response object is OK, or is there something wrong with doing it that I'm simply overlooking? For some reason it doesn't pass my "smell test" but I'm not sure why.....
I know this is an old thread, but there is something else to add to this topic.
Express has a response.locals object which is meant for this purpose - extending the response from middleware to make it available to views.
You could add a property directly to the response object, and as #hasanyasin indicated, is how JavaScript is designed. But Express, more specifically, has a particular way they prefer we do it.
This may be new in express 3.x, not sure. Perhaps it didn't exist when this question was asked.
For details, see
http://expressjs.com/en/api.html#res.locals
There is also an app.locals for objects which don't vary from request to request (or response to response I suppose).
http://expressjs.com/en/api.html#app.locals
See also: req.locals vs. res.locals vs. res.data vs. req.data vs. app.locals in Express middleware
It is perfectly OK. It is how JavaScript is designed. Only thing you should be careful is to not accidentally overriding already existing properties or being overridden by others. To be safer, instead of adding everything directly to req/res objects, you might consider going a level deeper:
res.mydata={}
res.mydata.person= ...
Like that.
Use res.locals for including custom variables in your response object.

Resources