Reputation: 67
I am struggling to really get a grasp on some fundamental basics here, and I feel it is not only holding me back, but resulting in crappy code and I don't like that.
I understand the concept of breaking out functional chunks of code into separate modules, like say routes, DB models, etc, but i'm having a real tough time understanding how to properly orchestrate the interdependent functioning of all these separate modules.
Let me give a fe examples of where my struggles lie.
My ExpressJS 'app' is setup in my main program module, just like you see in every tutorial. However I need to access the app instance in other modules as well. How do I do that? One way I learned from various tutorials is to make the entire module export a function which takes the app as a param, then do what I need in the function. But that seems to me to add a lot of complexity to things. Not only do I now have an entire module enclosed in a function, but I seem to lose the ability to actually export multiple functions, objects, or other variables out of that module.
Module as a Function
module.exports = function(app) {
blah;
};
Module without a Function
exports.func1 = function() {
}
exports.func2 = function() {
}
The latter gives me much more flexibility in my eyes, but I seem to be forced often to use the former, because I need to pass in things like the app from somewhere else.
I am using connect-rest for my REST API. All the code for my API lives in a separate module named simply 'api'. That has been fine until recently. Now I need to access a function that is in the api module, from inside my routes module. Presently my main routes are defined before my api, so I can't exactly pass my api export into my route function. I could reverse them probably, but this is only covering up a larger issue.
As my codebase grows, i'm finding it more and more frequent that various modules need to work with each other - it just isn't feasible to keep them all completely searate. Sometime it is possible, but it is unclean.
I feel like i'm missing some basic Node.JS (or maybe just Javascript) paradigm that is used to manage all of this.
If anyone could help me understand I would be most appreciative. I am an experienced developer in other languages such as C++ and Python if it helps to couch things in other terms.
I feel that I did not adequately communicate my intention for posting, so let me try and sum up my issue with a working problem.
server.js
var http = require('http'),
express = require('express'),
app = express();
// Bunch of stuff done with app to get it set up
var routes = require('routes.js')(app);
app.js
module.exports = function(app, express) {
var router = express.router();
// code for routes
app.use('/', router);
}
In the above example, routes are split off into their own module, but that module needs app
and express
objects from server.js in order to function. So, by my current understanding, the only way to get those over into routes.js is to make routes.js export one big function which you then call with the two objects you need.
However, what if I want routes.js to export multiple functions that might be used in other places? By my understanding I now can't. What if I Wanted to do:
authentication.js
var routes = require('routes');
// setup auth
routes.doSomethingFunction(auth);
I can't do that because routes is only exporting that one mega function.
Upvotes: 2
Views: 1103
Reputation: 707148
Each node module is simply an object. The part of that object which is available to the outside world is the module.exports
object which contains properties which can be functions or data.
The require("xxx")
command gets you the exports
object for that module (from a central cache or loads it from the .js file is it hasn't yet been loaded).
So, code sharing is simple. Just have each module do a require()
on any other modules that it wants to share code from and have those modules make sure the shared functions are accessible via it's own exports
object. This allows each module to essentially be stand-alone. It loads any other code that it needs and makes it a lot easier to reuse code. Modules are cached so doing lots of require()
operations on the same module from lots of other modules is nothing more than a cache lookup and is not something to worry about.
Data sharing (such as your app
object) can be accomplished in several different ways. The most common way is when you load the module to just call some sort of initialization function for the module and pass it any data that it might need. That would be the push model. Or, you can also do the pull model where a module asks another module for some piece of data.
All of this is a lot easier with the right code organization. If you start to feel like you have a spaghetti or interdependence, then perhaps you don't have the right code organization or you're just a bit too shy on just using require()
to pull in everything a given module needs. Remember each module will load whatever it needs itself so you only have to worry about what you need. Load those modules and they will load what they need.
You may also want to think more in terms of objects so you put most properties on some sort of object rather than lots of loose, individually shared variables. You can then share a single object and it automatically makes all the properties of that variable available to whomever you shared it with.
As to your question about sharing the app
object with another module, you can do that like this:
// in your app module
var express = require('express');
var app = express();
var otherModule = require('otherModule');
otherModule.setApp(app);
// now otherModule has the singleton `app` object that it can use
// in this case, otherModule must be initialized this way before it can do its job
In this example, I just used a single method .setApp()
to set the app object. That means all the other methods are available for other access into that module.
This could have also been done with a constructor-like method:
// in your app module
var express = require('express');
var app = express();
var otherModule = require('otherModule')(app);
This works also because the constructor can then return an object with other methods on it if you want. If you want to be able to get access to otherModule
from within other modules, but obviously you only want to initialize it just once and not in those other places, then you can either do this:
var otherModule = require('otherModule')();
from those other modules and have the constructor just check that if nothing is passed to it, then it is not getting the app
object from this constructor call so it should just return an object with other methods. Or, you can use the first code block above that returns all the methods from the initial require()
. You're totally free to decide what to return from the require()
. It can be just a constructor-like function which then returns another object when it is called. It can be just an object that has methods on it or (because functions are objects that can also have properties), you can even return a constructor-like function that ALSO has methods on it (though this is a bit less standard way of doing things).
And, your constructor function can decide what to do based on what is passed to it, given it a multitude of different behaviors based on what you pass to it.
Upvotes: 2