Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.js changing exports on the fly

changing exports.X in a function seems to not work...

I want to be able to load settings from a file & access them in Node.js. I have this currently, however, the clients connecting to my node application can edit what's in the settings file. Unfortunately as it stands the Node application has to be restarted for the changes to take effect. Is there a way I can reload the module.exports on the fly?

EDIT:

Settings file is literally a JSON string.

My settings module is 'required' in almost every single file, and there's a lot of files... So reloading it per-file basis is out of the question. I do, however, know precisely when someone makes a change to the settings.

like image 226
2778 Avatar asked Jul 04 '14 18:07

2778


2 Answers

If you are using require to load the settings and only referencing the settings from one module, then doing something along the lines of:

delete require.cache[require.resolve(filename)];

will work for you.

If, on the other hand, multiple modules will be referencing these settings, that approach can become a bit unwieldy and open you up to unforeseen bugs. For example, if any of the modules are holding on to a reference to the required settings file, they would each need to somehow learn that the settings had changed and update their references.

To alleviate (not completely solve) the caching issue, you build your settings interface so that users of it must access either the settings object via a function and/or require that properties are accessed via functions. Even with this model, someone may still decide to cache a setting causing an obscure failure later down the road.

Using the simplest approach of a single getter for the settings object would look something like this:

var settings = require('./settings.json');

// ... watch for changes and reload by invalidating node's cache

module.exports = function() { return settings; }

Usage:

var settings = require('./path/to/settings');

settings().foo;

There are several libraries that do settings. Depending on your needs, I'm partial to nconf.

like image 190
dc5 Avatar answered Nov 20 '22 16:11

dc5


I'd set up a file watcher here that checks for changes of a JSON file dynamically. It is not recommended practice to change a JS script once the app is running.

Something like:

var _ = require("lodash");
var fs = require("fs");
var result = {};

fs.watch('my-settings.json',function(event,filename){
   fs.readFile(filename,function(err,data){
       if(err){
          // your error catching
       }
       _.extend(result,JSON.parse(data));
   });
});

module.exports = result;

Now, this comes with lots of caveats, first that fs.watch is not always supported by all platforms. http://nodejs.org/api/fs.html#fs_fs_watch_filename_options_listener

Second, that it's really awkward to change a property like this. The expectation is generally that exports of module not mutate. I'd instead recommend exposing a method whose result can change based on the state of the file, a getter for the resulting data.

Third, a file watcher can be expensive, memory-wise.

This is better code, IMHO:

var _ = require("lodash");
var fs = require("fs");
var filename = 'my-settings.json';
var lastModified;
var mySetting;    

module.exports = {
    getSettingAsync : function (callback) {
        fs.stat(filename,function(err,stat){
            if(stat.mtime == lastModified) {
                callback(mySetting);
            } else {
                fs.readFile(filename,function(err,data){
                   if(err){
                      // your error catching
                   }

                   // this assumes that your data is always correct
                   mySetting = JSON.parse(data).mySetting;

                   callback(mySetting);
               });
            }
        });
    }
};

In this case, we both check for a JSON file, and expose this as an async method. You could just as easily change the code to use the sync versions if need be and return the value instead of invoking the callback. This version checks when the file was changed, which is cheaper than reading the whole file every time, reads the file if newer and saves you the need to use a potentially buggy file watcher.

By the way, I've not tested this code and it may contain errors as is, but the concept is sound.

But, perhaps the more salient question, why not just store that value in the database?

like image 22
Thinking Sites Avatar answered Nov 20 '22 17:11

Thinking Sites