nodejs - express how it is possible to make the global variables on all routes? - node.js

how it is possible to make my LANG is global on all routes ?
ex:
app.post('/set/lang',function(req,res){
req.lang = 'en';
res.json({lang:'en'});
});
app.get('/get/lang',function(){
res.json({lang:req.lang});
});
but return // {lang:undefined} ??
how I can do this? > /get/lang => return {lang:en}

req.lang is undefined becasue the request to /get/lang is stateless and does not know about the state of the request done on /set/lang.
What you need here is a session service such as https://github.com/expressjs/session
you may want to read more here http://expressjs-book.com/forums/topic/express-js-sessions-a-detailed-tutorial/
My understanding being that you want to save user language foreach user and let them set it via a sort of language selector in your client app.
Note also, the question is confusing as it may also refer to global named parameters in expressjs which you could implement with https://github.com/expressjs/express-params.
Or, it may also refer to an input content validation, see more at http://expressjs.com/4x/api.html#app.param
Finally the version is not provided i assume it is the latest 4.x branch.

You should use app.locals & res.locals.
There is a good explanation here:
app.locals and res.locals life cycle

Related

How to implement different middlewares for different monk's Manager instance?

I'm using monk (https://automattic.github.io/monk/) in order to do mongodb data manipulation.
Monk has a middleware mechanism, and we can simply invoke addMiddleware to add as many middleware as needed (https://automattic.github.io/monk/docs/middlewares.html)
It works well until I create two manager (https://automattic.github.io/monk/docs/manager/) instance. The middlewares of the two manager seems to polute each other.
So, I create a simple test in order to make sure the hypothesis.
Here is the test:
let db = monk('mongodb://localhost/test')
let softDb = monk('mongodb://localhost/other')
console.error(db._collectionOptions.middlewares === softDb._collectionOptions.middlewares)
And as my prior hypothesis, it yield true. Eventhough db and softDb are different objects, they seems to share the same _collectionOptions.middlewares. Probably monk's developer implement singleton pattern.
My question is: how could I make this softDb and db have different set of middlewares?
Thanks in advance.
I find a workaround. This is not an officially approved solution and I'm still seeking for a better solution. So, if any of you find a better solution, I'll approve your answer.
Since both softDb and db share the same set of middleware, then I deep-clone and re-assign both db._collectionOptions.middlewares. For deep-cloning purpose, I use this package: https://www.npmjs.com/package/clone.
The solution looks as follow:
let clone = require('clone')
let db = monk('mongodb://localhost/test')
db._collectionOptions.middlewares = clone(db._collectionOptions.middlewares)
let softDb = monk('mongodb://localhost/other')
softDb._collectionOptions.middlewares = clone(softDb._collectionOptions.middlewares)
// Here I can add different set of middlewares to db and softDb
// The middlewares of both manager will be independent to each other
console.error(db._collectionOptions.middlewares === softDb._collectionOptions.middlewares)
// Now, this will yield `false`
It is important to note that you have to copy the middlewares immediately after creating a manager instance.
I'm aware that some people might hate this solution. So, rather than down-voting this answer, please provide a better solution instead.

Organizing Node Express App in language subpaths

I'm in the progress of internationalizing an node expressjs-app. The idea is to serve the whole website within a language-subfolder (www.domain.com/en/). I'm kinda struggling wrapping my head around how to organize the app and it seems kinda hard to find some useful resources on this issue on stacko or the web in general.
I feel what makes it challenging here, is the fact that you have to achieve the best in three areas: Usability, SEO, and Performance.
There are a couple of questions:
Where are the response/selected languages to be stored? In the Session?
What is ideally the single source of throuth of the current language setting?
How is the language path affecting the language? (Changing the language to current path? Redirect to active/stored language?)
How are the routes to be organized? What Middleware strategies make sense for detecting and changing languages? Is it necessary to add to all internal links the language subpath, or can this be done by clever routing?
I would love to get some hints, resources, blog articles, repos where I can learn about best practices on this topic.
Cheers
I can highly recommend i18n for node.js. I have used it in 2 node Projects so far and it always worked like a charm. I then always had one json-File for each language I wanted to serve. You need to implement it once in your templates and then it hsould just work.
Regarding configuration an easy example:
// Configuration
app.configure(function() {
[...]
// default: using 'accept-language' header to guess language settings
app.use(i18n.init);
[...]
});
So than i18n will guess the language based on the users browser agent.
What I am always doing is not using an extra route rather I am using a parameter lang and than it is possible to change the language all the time per hrefs. E.g. https://example.com/?lang=de will change to german language. Of course you need in every get of express a helper function to detect if another language is set and then you can handle that. For me that was the easiest way but regarding SEO that is not the best I think.
Of course you can also handle it e.g. with different domains/subdomains as airbnb is doing that. https://nl.airbnb.com vs. https://www.airbnb.com vs. https://www.airbnb.de or as you mentioned with routes does also work very well. But I think that is related with a little more work.
For pros and cons regarding SEO and other you can just google a little bit and have a look at this quora question which also highly recommends the Google Best Practices at this topic.
You don't even need to use a library for a simple localization. I'll show you a simple example:
Let's say you have your language strings in a json at global scope (can be in a file or db too) :
var languageData = {
'en': {
'LOGIN_BTN': 'Login now',
'REGISTER_BTN': 'Register'
},
'tr': {
'LOGIN_BTN': 'Giris',
'REGISTER_BTN': 'Kayit'
}
}
Let's create simple middleware:
function getLanguageStrings(req, res, next) {
var lang = req.acceptsLanguages('en', 'tr', 'fr')
var selectedLang = lang ? lang : 'en' // default to english
req.languageStrings = languageData[selectedLang]
next()
}
Above, I used acceptsLanguages() method to get the preferred language of browser, but you can set cookie from client side and read it in our middleware if you want to.
With the req.languageStrings = languageData[selectedLang] line, I've attached strings to current request so that next middleware can use it.
Let's use our middleware:
app.use(getLanguageStrings)
And in the route, render them to view:
app.get("/info", function (req, res) {
res.render("info.html", {
languageStrings: req.languageStrings
})
})
In view, you now use it with your preferred template engine:
<button class="btn">{{languageStrings.LOGIN_BTN}}</button>
<button class="btn">{{languageStrings.REGISTER_BTN}}</button>
For this purpose I used i18n module (pretty much the same procedure with other localization modules). You keep your translations in simple json files and by default i18n checks for a language depending on a cookie sent by client.
That is pretty much it, I think there is a few other ways to get the language instead of using cookies, for example by request params (as you've mentioned) or by value sent within request body.
It really depends on your needs. This is only available if you use i18n-node-2 module, for the first one you have to use cookies (correct me if I'm wrong).
Example I've created to show how to set it up on your server side.
Localization with Express and i18n
Update:
For i18n-node-2
Like the README.md file says, there is a few functions which you can choose to detect / set needed language:
setLocale(locale)
setLocaleFromQuery([request])
setLocaleFromCookie([request])
setLocaleFromSessionVar([request])
setLocaleFromEnvironmentVariable()
Documentation: i18n-node-2

Is it good practice to add database connection object to the process.env global in node.js?

We already use node's process.env Global Object to store/access environment variables but can we also attach other objects that need to be accessible across several files/modules ?
A concrete example:
Imagine you are connecting to a Datastore e.g. Redis1 and you are modularising your app so that each module would open its own connection to Redis.
Would it make sense to use the following:
process.env.redisClient = || process.env.redisClient || require('redis').createClient()
// use the shared/global connection for your needs:
process.env.redisClient.set('Hello', 'World');
process.env.redisClient.get('Hello', function(err, reply) {
console.log('Hello ' + reply);
});
I realise this question can be perceived as "subjective" or "opinion-based", but I'm not asking for anyone's opinion; I want to know if there are people with specific experience of cases where adding an object to process.env will "break production" or have another adverse effect.
1 its the same for any db where the number of connections should be minimised so applies to MySQL, PostgeSQL, MongoDB etc.
Sadly, this does not work.
We cannot attach an object or function to node's process.env Global object because all entries added to process.env are type-cast to String.
If you're interested in how we ended up solving this problem see: https://github.com/nelsonic/redis-connection

Replacement for req.param() that is deprecated in Express 4

We are migrating from ExpressJS 3 to ExpressJS 4, and we noted that the following APIs are being deprecated:
req.param(fieldName)
req.param(fieldName, defaultValue)
Is there a middleware that brings these APIs back, like other APIs that were 'externalized' from express to independent modules ?
EDITED:
Clarification - The need is an API that provides an abstracted generic access to a parameter, regardless to if it is a path-parameter, a query-string parameter, or a body field.
Based on Express Documentation, we should use like this
On express 3
req.param(fieldName)
On express 4
req.params.fieldName
Personally i prefer req.params.fieldName instead req.param(fieldName)
Why would you want to bring it back? There's a reason that it's been deprecated and as such you should probably move away from it.
The discussion on why they are deprecating the API is available at their issue tracker as #2440.
The function is a quick and dirty way to get a parameter value from either req.params, req.body or req.query. This could of course cause trouble in some cases, which is why they are removing it. See the function for yourself here.
If you are just using the function for url parameters, you can just replace it with this a check for req.query['smth'] or 'default':
var param_old = req.param('test', 'default');
var param_new = req.query['test'] || 'default';
(Please note that an empty string is evaluated to false, so they are not actually 100% equal. What you want is of course up to you, but for the most part it shouldn't matter.)
Ok, after reading the threads given in references by #Ineentho, we decided to come up with the following answer:
https://github.com/osher/request-param
A connect/express middleware to enable back the req.param(name,default) API deprecated in express 4
The middleware does not only brings back the goo'old functionality.
It also lets you customize the order of collections from which params are retrieved , both as default rule, and as per-call :-)
Have fun!

sails.js Use session param in model

This is an extension of this question.
In my models, every one requires a companyId to be set on creation and every one requires models to be filtered by the same session held companyid.
With sails.js, I have read and understand that session is not available in the model unless I inject it using the controller, however this would require me to code all my controller/actions with something very, very repetitive. Unfortunate.
I like sails.js and want to make the switch, but can anyone describe to me a better way? I'm hoping I have just missed something.
So, if I understand you correctly, you want to avoid lots of code like this in your controllers:
SomeModel.create({companyId: req.session.companyId, ...})
SomeModel.find({companyId: req.session.companyId, ...})
Fair enough. Maybe you're concerned that companyId will be renamed in the future, or need to be further processed. The simplest solution if you're using custom controller actions would be to make class methods for your models that accept the request as an argument:
SomeModel.doCreate(req, ...);
SomeModel.doFind(req, ...);
On the other hand, if you're on v0.10.x and you can use blueprints for some CRUD actions, you will benefit from the ability to override the blueprints with your own code, so that all of your creates and finds automatically use the companyId from the session.
If you're coming from a non-Node background, this might all induce some head-scratching. "Why can't you just make the session available everywhere?" you might ask. "LIKE THEY DO IN PHP!"
The reason is that PHP is stateless--every request that comes in gets essentially a fresh copy of the app, with nothing in memory being shared between requests. This means that any global variables will be valid for the life of a single request only. That wonderful $_SESSION hash is yours and yours alone, and once the request is processed, it disappears.
Contrast this with Node apps, which essentially run in a single process. Any global variables you set would be shared between every request that comes in, and since requests are handled asynchronously, there's no guarantee that one request will finish before another starts. So a scenario like this could easily occur:
Request A comes in.
Sails acquires the session for Request A and stores it in the global $_SESSION object.
Request A calls SomeModel.find(), which calls out to a database asynchronously
While the database does its magic, Request A surrenders its control of the Node thread
Request B comes in.
Sails acquires the session for Request B and stores it in the global $_SESSION object.
Request B surrenders its control of the thread to do some other asynchronous call.
Request A comes back with the result of its database call, and reads something from the $_SESSION object.
You can see the issue here--Request A now has the wrong session data. This is the reason why the session object lives inside the request object, and why it needs to be passed around to any code that wants to use it. Trying too hard to circumvent this will inevitably lead to trouble.
Best option I can think of is to take advantage of JS, and make some globally accessible functions.
But its gonna have a code smell :(
I prefer to make a policy that add the companyId inside the body.param like this:
// Needs to be Logged
module.exports = function(req, res, next) {
sails.log.verbose('[Policy.insertCompanyId() called] ' + __filename);
if (req.session) {
req.body.user = req.session.companyId;
//or something like AuthService.getCompanyId(req.session);
return next();
}
var err = 'Missing companyId';
//log ...
return res.redirect(307, '/');
};

Resources