59

I use separate router files as modules for main app and auth app. I can't get the best way to pass variables(db client) into routers. I don't want to hardcode it or pass it with:

module.exports = function(app, db) {

Maybe it's best way to use singleton register or use global db variable?

What is your experiense with design-patterns? Which way is the best and why?

Flip
  • 6,233
  • 7
  • 46
  • 75
Serg
  • 736
  • 1
  • 9
  • 20
  • 1
    possible duplicate http://stackoverflow.com/questions/8931239/how-to-access-variables-declared-in-main-app-js-in-seperate-route-files-in-node/8931366#8931366 – Jamund Ferguson Apr 24 '12 at 21:28

4 Answers4

110

I have found using dependency injection, to pass things in, to be the best style. It would indeed look something like you have:

// App.js
module.exports = function App() {
};

// Database.js
module.exports = function Database(configuration) {
};

// Routes.js
module.exports = function Routes(app, database) {
};

// server.js: composition root
var App = require("./App");
var Database = require("./Database");
var Routes = require("./Routes");
var dbConfig = require("./dbconfig.json");

var app = new App();
var database = new Database(dbConfig);
var routes = new Routes(app, database);

// Use routes.

This has a number of benefits:

  • It forces you to separate your system into components with clear dependencies, instead of hiding the dependencies somewhere in the middle of the file where they call require("databaseSingleton") or worse, global.database.
  • It makes unit testing very easy: if I want to test Routes in isolation, I can inject it with fake app and database params and test only the Routes code itself.
  • It puts all your object-graph wiring together in a single place, namely the composition root (which in this case is server.js, the app entry point). This gives you a single place to look to see how everything fits together in the system.

One of the better explanations for this that I've seen is an interview with Mark Seeman, author of the excellent book Dependency Injection in .NET. It applies just as much to JavaScript, and especially to Node.js: require is often used as a classic service locator, instead of just a module system.

Domenic
  • 110,262
  • 41
  • 219
  • 271
  • I'm trying to use this example, however, how do I define my routes in your implementation of Routes.js since it's inside this Routes function? Thanks! – cpeele00 Jan 25 '13 at 19:01
  • @cpeele00 the same way you would any other class's methods. – Domenic Jan 27 '13 at 01:35
  • When using this setup, do you separate out your routes in a similar way? e.g. Routes(app, database) { routes = { social: new SocialRoutes(app, database), api: new ApiRoutes(app, database) }; return routes; } – Stephen Jul 17 '13 at 10:53
  • 1
    I like this style as well. I built [Electrolyte](https://github.com/jaredhanson/electrolyte), which does exactly this, while eliminating the manual plumbing needed to pass dependencies throughout the application. – Jared Hanson Nov 26 '13 at 05:56
  • 4
    In order to keep a singleton for the whole app is it better to do function App(){...} module.exports = new App();? If you do this you are caching JUST the new App() and not the function so you can ensure that require() caching will keep your obj a singleton for the whole app. See: http://bites.goodeggs.com/posts/export-this/#singleton – alph486 Nov 17 '14 at 21:07
  • 2
    @alph486 that is the opposite of dependency injection, and I do not recommend it. Singletons are in general an anti-pattern. – Domenic Nov 18 '14 at 04:41
  • @Domenic - I see your argument in terms of "global scope". But in general this is how spring contexts work isn't it? The framework constructs you an object the way you specify and it is delivered the same way to all callers. The framework also maintains the lifecycle of the objects too. I'm not an expert in node by any means but from what I understand it seems similar. Anyway just a thought. Thanks for the pattern. – alph486 Nov 18 '14 at 04:49
2

I suggest you create a settings file with db instance and with other things which you need use globally like 'singleton'.

For example, I have settings.js with my redis db client:

var redis = require('redis');
exports.redis = redis.createClient(6379, '127.0.0.1');

And in other multiple modules I include it:

var settings = require('./settings');
setting.redis.<...>

Many time including it I always have one instance of db connection.

akaravashkin
  • 424
  • 4
  • 9
  • If i use next code: ` var redis = require('redis'); var client = redis.createClient(6379, '127.0.0.1'); client.auth(redispass); exports.client = client; ` will this module reconnect redis again and again, or not? – Serg Apr 25 '12 at 21:16
  • 2
    no, only one connection. Look for [documentation](http://nodejs.org/api/modules.html#modules_caching), _Multiple calls to require('foo') may not cause the module code to be executed multiple times_ – akaravashkin Apr 26 '12 at 06:08
  • 3
    Its risky to rely on "require" for singleton behavior, as you will only get the same instance back if you pass exactly the same path. The modules are cached based on the passed path, not on a resolved path. In other words, it will work as a singleton, until you try to use it from a subdirectory. – Jolly Roger Feb 26 '14 at 18:14
  • 2
    @JollyRoger This is not true. Modules are cached based on their resolved filename. See http://nodejs.org/api/modules.html#modules_module_caching_caveats – Ilya I Dec 07 '14 at 11:57
  • @Ilya Ok, technically correct - but it's the resolved filename relative to the calling module, which (per my comment) will be different if you do it from a subdirectory. So, I stand by my "risky" assessment. – Jolly Roger Jun 10 '15 at 21:47
1

You can save yourself all the boilerplate code of wiring up your modules if you use a dependency injection framework

This answer lists a few of them. I also built a simpler DI framework here.

EDIT: below is a copy form the answer in case that page changes


require is the way of managing dependencies in Node.js and surely it is intuitive and effective, but it has also its limitations.

My advice is to take a look at some of the Dependency Injection containers available today for Node.js to have an idea on what are their pros/cons. Some of them are:

Just to name a few.

Now the real question is, what can you achieve with a Node.js DI container, compared to a simple require?

Pros:

  • better testability: modules accepts their dependencies as input
  • Inversion of Control: decide how to wire your modules without touching the main code of your application.
  • a customizable algorithm for resolving modules: dependencies have "virtual" identifiers, usually they are not bound to a path on the filesystem.
  • Better extensibility: enabled by IoC and "virtual" identifiers.
  • Other fancy stuff possible:
    • Async initialization
    • Module lifecycle management
    • Extensibility of the DI container itself
    • Can easily implement higher level abstractions (e.g. AOP)

Cons:

  • Different from the Node.js "experience": not using require definitely feels like you are deviating from the Node way of thinking.
  • The relationship between a dependency and its implementation is not always explicit. A dependency may be resolved at runtime and influenced by various parameters. The code becomes more difficult to understand and debug
  • Slower startup time
  • Maturity (at the moment): none of the current solutions is really popular at the moment, so not so many tutorials, no ecosystem, not battle tested.
  • Some DI containers will not play well with module bundlers like Browserify and Webpack.
Community
  • 1
  • 1
gafi
  • 12,113
  • 2
  • 30
  • 32
0

It is completely outdated, but you can use global in a script :

 global.foo = new Foo();

in another script :

 foo.bar();

You can also use already existing constant :

 Object.foo = new Foo();

And here :

 Object.foo.bar();
Vinz243
  • 9,654
  • 10
  • 42
  • 86