141

Is it possible to pass arguments when loading a module using require?

I have module, login.js which provides login functionality. It requires a database connection, and I want the same database connection to be used in all my modules. Now I export a function login.setDatabase(...) which lets me specify a database connection, and that works just fine. But I would rather pass the database and any other requirements when I load the module.

var db = ...
var login = require("./login.js")(db);

I am pretty new with NodeJS and usually develop using Java and the Spring Framework, so yes... this is a constructor injection :) Is it possible to do something like the code I provided above?

Andreas Selenwall
  • 5,705
  • 11
  • 45
  • 58
  • I'd also recommend looking at the answers to [this](http://stackoverflow.com/questions/13080771/node-js-passing-variables) question. As pointed out in my answer, a common idiom is to pass the `app` object to required modules. – David Weldon Oct 31 '12 at 05:39
  • Instead of doing all this argument passing for db, you could use a [singleton](http://www.dofactory.com/javascript/singleton-design-pattern) implementation and call db.getInstance() where needed. – wulfgarpro Nov 06 '15 at 22:29
  • use `db` as an argument to the function inside your export file – mercury Jun 01 '21 at 06:55

3 Answers3

254

Based on your comments in this answer, I do what you're trying to do like this:

module.exports = function (app, db) {
    var module = {};

    module.auth = function (req, res) {
        // This will be available 'outside'.
        // Authy stuff that can be used outside...
    };

    // Other stuff...
    module.pickle = function(cucumber, herbs, vinegar) {
        // This will be available 'outside'.
        // Pickling stuff...
    };

    function jarThemPickles(pickle, jar) {
        // This will be NOT available 'outside'.
        // Pickling stuff...

        return pickleJar;
    };

    return module;
};

I structure pretty much all my modules like that. Seems to work well for me.

floatingLomas
  • 8,553
  • 2
  • 21
  • 27
  • Is the `app` argument necessary or can I ommit it? My module won't make explicit use of this app argument, but I don't know if it is required by node.js for some internal thing. If it's ok, my module declaration would look like this: `module.exports = function (db) {` – Ulysses Alves Nov 11 '15 at 12:09
  • 1
    That was specific to an Express app, so definitely not necessary. – floatingLomas Nov 12 '15 at 20:59
  • @floatingLomas The serialization mechanism in python is called pickle: http://stackoverflow.com/questions/11218477/how-can-i-use-pickle-to-save-a-dict – SadSeven Jul 19 '16 at 11:10
  • 1
    So what happens if you reference the module several times? The first `require(mymodule)(myargs)` would give you a module to work with. However if you reference it somewhere else from another module?? in the basic system there appears to be a cache involved, but in this system, the cache would return the bare generator method on subsequent calls to `require()`, and if you passed args to `require()(someargs)` you would get a different module back... maybe I am missing something – Tom Dec 15 '17 at 22:21
  • 2
    @TomH In that case, you'd want to cache it. To do that you'd have `var module;` outside the exports function, and then as soon as you come into the you'd want to check if `module` is already defined, and if so, just return it (and if not, initialize it). Does that make sense? – floatingLomas Dec 21 '17 at 22:04
  • @floatingLomas: you mean something like `var instances = {}; module.exports = name => instances[name] ? instances[name] : instances[name] = name + ": " + Math.floor(Math.random() * 10000);` If you require this module several times with the same argument, you'll get a single instance. – Nicolas Le Thierry d'Ennequin Mar 29 '18 at 10:40
  • Is there a way to solve @TomH's problem without the `var` approach, in a way that wouldn't give editors a hard time figuring out autocomplete? – Danyal Aytekin May 10 '18 at 13:03
  • There's no way to solve caching the initially created result without caching the initially created result, no. The approach to solve TomH's problem doesn't affect how the module is used elsewhere; it merely changes the first three lines to something like: `var module; module.exports = function (app, db) { if (module) return module; ` so that if the `module` was already defined, the previously created one is returned instead of being again created. – floatingLomas May 11 '18 at 15:56
52

I'm not sure if this will still be useful to people, but with ES6 I have a way to do it that I find clean and useful.

class MyClass { 
  constructor ( arg1, arg2, arg3 )
  myFunction1 () {...}
  myFunction2 () {...}
  myFunction3 () {...}
}

module.exports = ( arg1, arg2, arg3 ) => { return new MyClass( arg1,arg2,arg3 ) }

And then you get your expected behaviour.

var MyClass = require('/MyClass.js')( arg1, arg2, arg3 )
liamvovk
  • 695
  • 6
  • 4
34

Yes. In your login module, just export a single function that takes the db as its argument. For example:

module.exports = function(db) {
  ...
};
David Weldon
  • 63,632
  • 11
  • 148
  • 146
  • 1
    I tried this out. In login.js `module.exports = function(app,db) { ... } module.exports.auth = function(req,res) { ... authentication stuff }` It do call the "anonymous" function and sets the `app` and `db` variables, but, when doing this my application won't find the `auth` function. What am I doing wrong? If I remove the anonymous function the `auth` function becomes accessible again. – Andreas Selenwall Oct 31 '12 at 06:46
  • 1
    Like everything in javascript, `exports` is an object. You can assign properties to it (multiple exports), or you can assign it to a value. It sounds like you assigned exports to a function, but then assigned auth as a property of the function you assigned to exports. So you'd need to do `var auth = require("./login.js").auth` which may not be what you intended. If you want to use the pattern from the original question, its probably best to stick to a single export value. If this still does not make sense, I'd recommend posting a gist for me to look at. – David Weldon Oct 31 '12 at 07:33
  • 1
    After reading this again, it sounds like you may have assigned `auth` as a property of the `exports` object, and then later in the module you assigned `exports` to a function (thus overriding the previous assignment). If you reverse the order of the assignment you should be able to access the `auth` function as you'd expect. Again it's hard to tell without actually seeing the code. – David Weldon Oct 31 '12 at 07:43
  • 1
    Thanks alot for your help. What I didn't realize was exactly what you were aiming for with require('login').auth. I though there were no difference in `var login = require('login')` and `var login = require('login')(app)`, but there is a huge difference, there is no magic in the anonymous function returned, it is just another function/object. Instead of having a `module.exports.auth`, the anomymous function now returns the auth function (amongs others), i.e. `return { auth: authFunction, login: loginFunction}`. So now it works. Thanks. – Andreas Selenwall Oct 31 '12 at 15:29