In my app.js file, I use app.set(valName, value) to pass several config values to my routes.
I then pass the app to my routes: app.use('/', require('./routes/index')(app));
I can then easily use app.get(valName) to retrieve the values in the routes.
My question is: wouldn't it be easier to just use app.valName = value; and then just access app.valName in the route?
Do I have to use the .set and .get? Or will this be violating something?
Do I have to use the .set and .get? Or will this be violating something?
Do you have to? No. Should you? Yes!
By setting your application properties directly to the app object (e.g. app.valName = value;), you may be overwriting properties that Express has initialized for internal use.
You can see from Express's source code that Express applications create the following properties in their init function:
this._baseRoutes = {};
this.cache = {};
this.settings = {};
this.engines = {};
this.defaultConfiguration();
And even more properties are set after the defaultConfiguration function is executed. This means that if you want to have a property named cache, settings, or engines, just to name a few, you will be stepping on Express's toes and will probably cause something to go wrong when your application is running.
You can see from the source of app.set that application settings are sorted in a settings object, not directly in the application object. This is to prevent what I just described above: conflicts with internal property names.
Related
I use debug strings for debugging Loopback 2.0 application. Loopback documentation says:
The LoopBack framework has a number of built-in debug strings to help
with debugging. Specify a string on the command-line via an
environment variable as follows:
MacOS and Linux
$ DEBUG=<pattern>[,<pattern>...] node .
Is it possible to change patterns dynamically in runtime? Or is it possible to use environment-specific configuration?
Before I get deeper note that this debug logging facility uses visionmedia's debug module which handles almost all of the logic.
Is it possible to change patterns dynamically in runtime?
Well before any module is loaded, safest and best way I believe is to just manipulate the environmental variables:
if (process.env.NODE_ENV === 'development') {
process.env.DEBUG = process.env.DEBUG + ',loopback:*';
}
Another way would be to load debug and use it's .enable method:
require('debug').enable('loopabck:*');
But note that it only works if you do this before Loopback is required since it only allows changes before it's instances are created, which is in this case before loopback is loaded. Another thing is that, there might be multiple debug modules installed depending on the dependencies and your package manager(npm#3, npm#2 and yarn behave differently). debug might be in your node_modules directory, or it might be in loopback each module, node_modules directory. So make sure you require all instances of it and enable, if you want to do it this way.
Now if don't want to do it on the startup, well API doesn't allow changes in the runtime. You can view the discussion regarding this here. Though there are some dirty ways to go around it, but these might possibly break in the future so be careful.
Firstly, there's a module called hot-debug which supposedly makes require('debug').enable work on previously created instances also, but when I tried it, it didn't work perfectly and it was buggy, but it's possible it might work fine for you.
If that doesn't work for you another way is to override require('debug').log method. If this is defined, debug will call this method instead of console.log with the formatted the arguments. You can set DEBUG=* and then filter it yourself:
require('debug').log = function (string) {
if (string.contains('loopback:security')) {
console.log(string);
}
};
This way will be slow in production though as all the debug output will be formatted before being filtered even though nothing might be outputted to console.
Another thing to override the require('debug').init method. This is called everytime a new debug instance is created. Since every debug instance uses an enabled property to check if it's enabled we can toggle that.
const debug = require('debug');
const { init } = debug;
const instances = [];
debug.init = function(debugInstance) {
init(debugInstance);
instances.push(debugInstance);
};
// You can call this function later to enable a given namespace like loopback.security.acl
function enableNamespace(namespace) {
instances.forEach(instance => {
instance.enabled = instance.namespace === namespace;
});
}
Though there's a lot of improvement can be done on this, but you get the idea.
I can change debug namespace dynamically with the hot-debug module.
In my app, I've just created a function for this :
require('hot-debug')
const debug = require('debug')
function forceDebugNamespaces (namespaces) {
debug.enable(namespaces)
}
// Usage
forceDebugNamespaces('*,-express:*,-nodemon:*,-nodemon')
In my case, I have a config file which allow me to set process.env.DEBUG but I needed to find a way to update debug namespaces without restarting my app (the config file is watched for changed by my app).
I'm having trouble deciding/determining what the standard/best approach for passing dependencies over to my Express routes would be. The problem I'm encountering is that some of my libraries have bindings associated with them ie. my session manager is hooked up to my redis queue client. My logs have configuration settings mapping them accordingly etc.
So here are my routes:
// Include routes and endpoints
app.use(require('./routes/account')(queue, models, log));
app.use(require('./routes/message')(queue, models, log));
app.use(require('./routes/admin') (queue, models, log));
As you can see I'm passing in my dependencies as parameters. I haven't started writing my unit tests yet, but I suspect this will bring more painful when I get there.
The other thing I've thought of doing is attaching the libraries to my request object as such:
req.log = log;
req.nconf = nconf;
req.sessions = sessions;
What I'm unsure of is: a) what the best/standard practice is, b) do any of these methods impact performance/memory usage, c) how will this impact my future unit testing.
Any insights on this would be great!
Thanks.
I would set them at the app level:
app.set('queue', queue)
Then pass your app to your routing functions:
app.use(require ('./routes/admin')(app))
Then retrieve:
queue = app.get('queue')
You might also consider requiring the libraries at the top of the file as needed.
So in the example with the log it would be something like:
var log = require('myLogger');
// or require('../path/to/logger') if it's your own module
Then you can go on to use the log in that file.
In the case of models, it makes it more clear by just including the one you need.
Ex:
// messageHandler.js
var Message = require('../models/message');
function sendMessage(params){
// do some logic on params
Message.send(params);
}
This works well for unit testing, because you can require things as needed, and also stub things by just changing which file you're requiring.
I hope this helps!
I have a node toplevel myapp variable that contains some key application state - loggers, db handlers and some other data. The modules downstream in directory hierarchy need access to these data. How can I set up a key/value system in node to do that?
A highly upticked and accepted answer in Express: How to pass app-instance to routes from a different file? suggests using, in a lower level module
//in routes/index.js
var app = require("../app");
But this injects a hard-coded knowledge of the directory structure and file names which should be a bigger no-no jimho. Is there some other method, like something native in JavaScript? Nor do I relish the idea of declaring variables without var.
What is the node way of making a value available to objects created in lower scopes? (I am very much new to node and all-things-node aren't yet obvious to me)
Thanks a lot.
Since using node global (docs here) seems to be the solution that OP used, thought I'd add it as an official answer to collect my valuable points.
I strongly suggest that you namespace your variables, so something like
global.myApp.logger = { info here }
global.myApp.db = {
url: 'mongodb://localhost:27017/test',
connectOptions : {}
}
If you are in app.js and just want to allow access to it
global.myApp = this;
As always, use globals with care...
This is not really related to node but rather general software architecture decisions.
When you have a client and a server module/packages/classes (call them whichever way you like) one way is to define routines on the server module that takes as arguments whichever state data your client keeps on the 'global' scope, completes its tasks and reports back to the client with results.
This way, it is perfectly decoupled and you have a strict control of what data goes where.
Hope this helps :)
One way to do this is in an anonymous function - i.e. instead of returning an object with module.exports, return a function that returns an appropriate value.
So, let's say we want to pass var1 down to our two modules, ./module1.js and ./module2.js. This is how the module code would look:
module.exports = function(var1) {
return {
doSomething: function() { return var1; }
};
}
Then, we can call it like so:
var downstream = require('./module1')('This is var1');
Giving you exactly what you want.
I just created an empty module and installed it under node_modules as appglobals.js
// index.js
module.exports = {};
// package.json too is barebones
{ "name": "appGlobals" }
And then strut it around as without fearing refactoring in future:
var g = require("appglobals");
g.foo = "bar";
I wish it came built in as setter/getter, but the flexibility has to be admired.
(Now I only need to figure out how to package it for production)
I'm new to node js. I searched a lot on stack overflow on this question below, none what I need.
I have an app.js file which initiates node server and a router file. I want to be able to store a global value once and shared across other server side .js files which contains my functions. I also want this variable to be accessible in my .jade file. (I use express BTW)
Is there a way to accomplish this?
Thanks.
The Node.js documentation says under Module Caching
Caching Modules are cached after the first time they are loaded. This means (among other things) that every call to require('foo') will
get exactly the same object returned, if it would resolve to the same
file.
Multiple calls to require('foo') may not cause the module code to be
executed multiple times. This is an important feature. With it,
"partially done" objects can be returned, thus allowing transitive
dependencies to be loaded even when they would cause cycles.
If you want to have a module execute code multiple times, then export
a function, and call that function.
Which means you can easily expose a global object simply by putting it in its own module.
//config.js
var config = {
dbUrl: 'mogodb://localhost:2107/persons'
};
module.exports = config;
And then when you want to gain access to that object, you simply do:
var config = require('./config');
And that's done, you get access to the same instance everywhere.
You'll want to limit the usage of global vars in Node. This is because unlike any other server side language, Node is a persistent process that share all request. So you cannot setup user state globally as those will be shared across all user accessing your site.
In raw node, there's two global context:
global.foo = 'bar';
// and the process object
process.some_var = 1;
In Express, you can setup application wide vars using app.set
But, most of the time you'll want to share data by adding them to the request or the response objects. That is because those objects are "user" specifics, unlike the global namespace.
For the template, you'll always want to pass in the context:
app.render('email', Object.assign( aSharedObject, {
specific: 'values'
}));
i would use process.env or if you are using nconf put it into the app configuration as Jordan said, globals are BAD idea, also if you don't want to include nconf or any other conf module or use process.env then you can create a module and export a set of getters and setters to handle the value
I ran into an issue with my Nodejs application.
I have two different apps that are using shared library, which is located so that it is found one level up in node_modules. So i have this structure ./app1/app.js, ./app2/app.js and ./node_modules/shared.libs/index.js.
shared.libs in its turn has some other modules installed, like mongoose, redis etc. Plus some mogoose models with additional functions in them. All are exported from index.js like this:
exports.async = require('async');
exports.config = require('./config');
exports.utils = require('./lib/utils');
And then in apps i import them like this:
var libs = require('shared.libs');
var config = libs.config;
So after this code i can use config which is coming from that shared library.
This part was and is working just fine. But now i need to put additional layer on top of this library (read: provide more unified interface for both apps).
What i tried to do is to add some functions into index.js of shared library and then export the whole object with these functions. But whenever i try to call previously imported (by var libs = require('shared.libs');) object it says that libs is not defined.
What am i doing wrong here?
I generally want to keep other code the same, so i won't need to go over replacing require part everywhere, but at the same time provide additional functionality from shared library which will be available from that imported libs object.
this should definitely work:
module.exports = {
async: require('async'),
config: require('./config'),
utils: require('./lib/utils'),
foo: function () {
return 'bar';
}
};
reference like:
var libs = require('shared.libs');
console.log(libs.async);
console.log(libs.config);
console.log(libs.utils);
console.log(libs.foo);
console.log(libs.foo());
What makes me wonder is one of your comments above, that you get an error libs is not defined. That looks like you should have other unnoticed errors before.. during the the initialization of your shared.libs module..