[EDIT]
Thanks to Stafano that formalized my question in a better way:
You have a module
-) There are several files in this module
-) All these files depend on a configuration whose path is unknown to the module itself
-) This module does not do much on its own, and is meant to be used by other applications
-) These applications should inject a configuration path into the module before it can be used
So i have this module, used from another application. It's composed of other submodules and i want to configure it using a configuration object.
I already tried to inject the configuration in my submodels but i had the same problem exposed in the original question.
For example my module use mongoDB (with mongoose) as a store.
// app.js
// in the config object i have the URI to the mongo instance (in order to create a connection).
var myModule = require('myModule')(config);
// myModule.js
// files
// myModule/index.js expose the module's functionalities
// is the entry point so I create the mongoose connection
var mongoose = require('mongoose');
module.exports = function(config){
var connection = mongoose.createConnection(config.store.URL);
// I need to expose this connection to the others submodules.
}
// myModule/storeController.js contains the business logic that use the store (createItem, deleteItem, get...) and requrie mongoose and my Models (store in the models folder)
var mongoose = require('mongoose');
var Item = require('./models/item.js');
exports.createItem = function(item){
Item.save(item, function(err, item){
if (err) throw
...
});
}
// myModule/models/item.js
// In this module i need to use the connection application in the configuration.
var mongoose = require('mongoose');
var connection = // i don't know how to get it
var ItemSchema = new mongoose.Schema({
name: String
});
module.exports = mongoose.model('item', ItemSchema);
If I inject the configuration obj to the item.js i can't do the module.exports of my model.
I hope that this example can clarify my question, but the problem is the simple, expose an object after get it as a parameter.
[PREVIOUS]
I have a node.js application that require a module. This module accept the coniguration file path (a JSON file).
I need to load that configuration on require and expose it to the module.
How can I achieve this behavior?
Something like:
// app.js
var myModule = require('myModule')(__dirname + '/config/myModuleCnfig.json');
// myModule.js
module.exports = function(configPath){
var config = require(configPath);
module.exports = config; // This is wrong
}
Is there another way to get the configuration path, configure the module and share the configuration??
With "share the configuration" i mean that i want to give the possibility to other files of my module to use that configuration.
Thanks for any suggestions!
FINAL EDIT:
After many misunderstandings, your problem is finally clear to me. To summarise what's in the comments, here is the situation:
You have a module
There are several files in this module
All these files depend on a configuration whose path is unknown to the module
itself
This module does not do much on its own, and is meant to be
used by other applications
These applications should inject a
configuration path into the module before it can be used
Since you cannot modify dynamically what a module exports, you should use another approach. As with most situations that you encounter in programming, there is not one way which is always right, as much pedends on your requirements and limitations.
The easiest way to do this (which I don't recommend) is to use a global variable, which you set in your myModule.js file and will be used by the other files in your module. The biggest drawback of this approach is that you wouldn't be able to use multiple instances of the module at the same time with different configurations. Also, any other module could easily modify (deliberately or not) you configuration at any time, by simply changing the value of the global variable, so it's also a security risk.
A much better way, which will probably require more work on your part - depending on how many files you have - is to implement some kind of Inversion of Control (IoC). In your case, you could turn all your exports into functions that accept a config, and then initialise them by passing the active configuration after you require the module. I don't know the specifics of your implementation, so here is some sample code:
// submodule1.js
module.exports = function(config) {
// return something that uses the configuration
}
// myModule.js
var fs = require('fs');
var submodule1 = require('./submodule1');
var submodule2 = require('./submodule2');
// ...
module.exports = function(configPath){
var config = JSON.parse(fs.readFileSync(configPath));
var sm1 = submodule1(config);
var sm2 = submodule2(config);
return /* an object that uses sm1 and sm2 */;
}
If your module is quite complex, you can use some IoC library that does the binding for you. An good one could be Electrolite.
Hope this helps.
PREVIOUS ANSWER:
You can use a library called jsop:
var jsop = require('jsop');
var config = jsop('./config/myModuleCnfig.json');
If you don't want to add a dependency to this module, the linked GitHub page also has a snippet that you can use to load the json config using only native methods.
EDIT: I just realised that this module is only for node 0.11, which you are probably not using. Since you don't probably need the writing functionality, you can use the following snippet instead:
var fs = require('fs')
var config = JSON.parse(fs.readFileSync('./config/myModuleCnfig.json'))
EDIT 2:
Now I think I understand your problem better. To pass the path to the required configuration, you can do something like this:
// myModule.js
var fs = require('fs')
module.exports = function(configPath){
var config = JSON.parse(fs.readFileSync(configPath))
return config;
}
Related
I have a javascript file for node.js:
module.exports = {
someString: 'blblalb'
}
I want to able to read the file as a javascript object, using fs.readFileSync. I can't use require because I am using a variable that may be modified in runtime to load the file.
Is that possible?
You can use eval('JavaScript string') but is highly recommended not to. It is a serious security risk if you cannot 100% trust the source of the text. If a malicious user figures out a way to modify the text they have complete control of your system. It is not a path I would take or recommend.
const text = 'console.log("Hello")';
eval(text);
If I saw that code when I was doing a code review we would definitely be having some words.
it's possible to evaluate a file or string variable as child module in hacky yet valid way.
The problem is that Node.js module environment should be unaware of these operations. Child module may load other modules and contain require(...), it will fail if there is no require function (there is none if it is evaluated with vm) or it uses wrong module relative path (this will happen with eval), also there will be no dedicated module.exports for a module. This can be fixed by wrapping module source with Node.js module wrapper that was rewired to match child module location.
const fs = require('fs');
const Module = require('module');
const path = require('path');
const childModuleAbsPath = path.resolve('./foo/bar.js');
const childModuleBody = fs.readFileSync(childModuleAbsPath);
const childModuleObj = { exports: {} };
const { dir: childModuleDirname, base: childModuleFilename } = path.parse(childModuleAbsPath);
const childRequire = modulePath => module.require(childModuleAbsPath);
require('vm').runInThisContext(Module.wrap(childModuleBody))(
childModuleObj.exports,
childRequire,
childModuleObj,
childModuleDirname,
childModuleFilename
);
In this case childModuleObj.exports.someString === 'blblalb' after bar child module was evaluated.
This is XY problem that should be addressed in another way.
If the intention is to reevaluate a module with new variables, this can be done by invalidating Node module cache by modifying require.cache, e.g. with decache:
decache('./foo/bar');
const reloadedBar = require('./foo/bar');
Bear with me as I lead you through the process that elicited my question.
I'm working on a CLI app in node and I'm using objects to encapsulate my business logic using this pattern:
// my-project/lib/widget/myobject.js
var MyObject = function(x) {
this.x = x;
};
MyObject.prototype.getX = function() {
return this.x;
};
module.exports = MyObject;
I'm also testing these objects:
// my-project/test/lib/widget/myobject.spec.js
var MyObject = require('../../../lib/widget/myobject.js');
describe('MyObject', function() {
...
});
At one point I was unhappy with the naming and directory structure I had chosen. I found myself tediously counting those parent directory references (..) in several spec files when rewriting the relative paths. I figured there must be an easier way to reference a root directory containing these object definitions.
One of the recommendations I found here suggested "putting application-specific modules into node_modules".
Now, as I understand modules, they are the packages I download from npm and use in my project. They contain libraries of useful things with a single API exported to me when I call require. This is not how I view the simple single-purpose classes built specifically for the internal use of my application.
If you've stuck with me this far, thank you! Here is my question:
How do I make the internals of my application more "modular" so it properly follows the intent of the Node module system while remaining object oriented?
I'm not sure how suitable this is for production or for modules you plan on distributing but in your main file you could add this:
process.env.NODE_PATH = __dirname;
require('module').Module._initPaths();
which would let you always require modules relative to the folder containing your main file. I.e. if you had a file in:
library/some_file.js
then in tests/some_other_file.js you could just do:
require('library/some_file');
Or as an alternative you could add this in your main file:
global.__base = __dirname + '/';
and then in your other modules require using:
var MyObject = require(__base + 'my-project/lib/widget/myobject');
I recently had the idea of using winston logging in nodejs. I started configuring it and it started to grow. I use it in my index.js file, but it started to get big and then I separated that file in modules. I want to log things that happens in the modules also, but for that I'd need to either require and reconfigure the winston log every time, or inject the winston logger from index.js into the modules. I think the last way is better, but is there a better way? My idea is: suppose loggin is my configured logging object, here's what I would do in each myModule.js:
var logging;
module.exports.injectLog(loggingObject) {
this.logging = loggingObject;
}
module.exports.someFunctionThatNeedsToBeLogged() {
//do something
logging("doing some log");
}
Then in index.js I would require the module like this:
var myModule = require('./myModule.js').injectLog(loggingObject);
is this a good approach?
There are a few ways to achieve what you're asking, a couple which I believe are cleaner than what you're describing.
You could supply a dependency object to each module that requires logging when requiring it. You could even move that to a config module to keep index.js clean
// index.js
var winston = require('winston');
winston.configure({
// config etc etc
});
var deps = {
logger: winston
}
var someOtherModule = require("./some-other-module")(deps);
// someOtherModule
module.exports = function(deps){
var logger = deps.logger;
// return your module
}
That way you can easily add additional dependencies when they arrive.
I think my preferred way would be to write a light wrapper around the logger and require it wherever required.
// logger.js
const winston = require('winston');
winston.configure({
// config etc etc
});
module.exports = winston;
// index.js
var logger = require("./logger");
// some-other-module
var logger = require("./logger");
I'm currently working on a small console project that depends a lot on the arguments that are passed initially and I'm looking for a good way to handle a configuration object in nodejs.
I have the project currently fully working with the following example but I think I'm relaying on the caching of modules when using 'require'.
lets assume a module options.js
'use strict';
var options = {
configName: '.jstail',
colorActive: (process.platform === 'win32') ? false : true, // deactivate color by default on windows platform
quiet: false,
debug: false,
config: null,
logFile: null,
setting: null
};
module.exports = options;
And my initial module init.js
#!/usr/bin/env node
'use strict';
var options = require('options'); // require above options module
// modify the options object based on args
I then have a logger that depends on this options
For example if quiet is set to true no logging should happen
logger.js
'use strict';
var options = require('options');
/**
* prints to console if not explicitly suppresed
* #param {String} text
*/
function log(text) {
if (!options.quiet) {
console.log('[LOG]: ' + text);
}
}
My big problem is (I think) that I'm relaying on the caching of nodejs modules when I require the options module in the logger
So my two questions are:
Am I right that this only works because of the caching of the modules that nodejs does for me?
Is there any better way to handle a dynamic global configuration?
I know there are several questions and tutorials around with a config file but thats not what I'm looking for.
Yes, this only works because of caching, though I wouldn't call it caching (but node.js docs do) rather than lazy initialization. It's ok to rely on that, a lot of modules do some initialization of first require, using it for configuration is also typical. Generally speaking, require is a node.js way of accessing global singleton objects.
The other way to do it is to load configuration from a single file, modify it and then pass it to other modules who need it, like this:
//index.js
var config = require('./config')
config.flag = false
var module1 = require('./module1')(config)
//module1.js
module.exports = function (config) {
// do stuff
}
It makes code more decoupled and testable but adds complexity. Difference between these two approaches is basically the same as using globals vs dependency injection. Use whatever you like.
I have one module which wraps the Socket.io functionality my app is using which looks something like this:
// realtime.js
var io = require('socket.io'),
sio;
exports.init = function(expressServer) {
sio = io.listen(expressServer);
}
...
The main app.js file looks like
// app.js
var rt = require('./realtime.js'),
other = require('./other.js');
...
rt.init(expressServer);
The other module also uses rt.js
// other.js
var rt = require('./realtime.js');
...
My question is, will both other.js and app.js have the same instance of rt.js?
The answer on SO relating to redis lead me to believe the above statement is true, but in the documentation here it says
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.
which seems to imply that it's not guaranteed to be the case?
Finally this question appears to indicate it depends on filename and that since there is only one instance of rt.js it shouldn't be executed more than once. If that's the case does it depend only on rt.js being the same file or does it depend on the path specified by require. Basically if rt.js and other.js were in lib/, and app.js was one level down the requires in other.js and app.js would point to rt.js from different files, does this matter?
I'd be grateful if anyone could clear this confusion up for me!
modules are currently evaluated only once, but you should not rely on this. Having any state in module is considered bad practice. What prevents you from passing reference to sio to other.js?
//realtime
var io = require('socket.io'),
exports.init = function(expressServer) {
return io.listen(expressServer);
}
// app.js
var rt = require('./realtime.js'),
other = require('./other.js');
...
var sio = rt.init(expressServer);
// now ask other.js to use same sio instance
other.use_sio(sio);
Be sure not to install socket.io in more than one place. If you require socket.io in different modules where each module is searching for packages from different paths, then each module will load a seperate instance of the package.
app directory layout:
-module1
--/npm_modules //has socket.io
---/socket.io
--/module1.js //requires socket.io from module1/npm_modules
-module2
--/npm_modules //has another socket.io installation
---/socket.io
--/module2.js //requires socket.io from module2/npm_modules (Does not create a reference to what was required in module1.)
Hope this helps.