Node.js Handbook - Designing Singletons
Update Dec. 18, 2014: A much needed update to this article has been posted to The Node Way, a new series on the design patterns and best practices needed to build beautiful Node.js applications. Check it — and a ton of other great resources — out at thenodeway.io.
In most languages, sharing an object across your entire application can be a complex process. These Singletons are usually quite complex, and require some advanced modifications to get working. In Node, however, this type of object is the default. Every module that you require is shared across your application, so there’s no need for any special classes or extra code.
Node doesn’t imposes much structure on it’s developers, so it’s up to you to choose the right design for your modules. In this post, I’ll share a few simple patterns that will help you get the most out of your singletons by designing them as cleanly and clearly as possible.
Private vs. Public
When creating a new module, your variables and functions are kept private by default. You’ll need to use module.exports to define your interface and make them public. This is a strait-forward process: whatever you set to
module.exports becomes public, and is visible when your module is required.
1 2 3
1 2 3
Your goal when designing modules should be to keep as much code private as possible, and only export what needs to be referenced by the rest of your application. Public functions can still reference private variables and functions, so there’s no harm in being conservative when choosing between the two.
Exporting a Single Source of Truth
There are a million different decisions to make when designing your module. Where should I declare my functions? Where should I export my public interface? Should I declare all my public functions as properties of module.exports directly, or one by one?
While none of these choices have right or wrong answers, the decisions you make will have a major effect on the clarity of your code. Declaring and exporting properties one at a time, for example, leaves you scanning each function declaration to understand the public interface. Exporting all of your functions at the bottom means you have to jump around your file to see what’s public. Neither design gives you the full picture, and both are burdensome to understand.
To get the most out of your code, keep a single source of truth to your design. You should be able to look in one place to see exactly what’s public, what’s private, and what the hell is going on. Kill two birds with one stone: define AND export your public variables at the same time. You’ll have an explicit separation of private and public as well as a complete picture of your public interface, instead of constantly searching your code base to find which functions are being exported.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
var self = module.exports
While defining your public functions within
module.exports leaves you with a great design, it isn’t without its own set of problems. For one, how do you call your own public functions & variables? Do you really have to call
module.exports.x every time you want the value of x?
Luckily we can make an alias of our own. Defining
self to be an alias to
module.exports will help save you some characters while still maintaining your single point of truth.
1 2 3 4 5 6 7 8 9 10
We choose self because it feels so similar to
this while still distinguishing itself as an alias of our own creation. Of course, feel free to name it however you’d like.
Avoiding the Exports Alias
A seasoned Node veteran will know there’s a second way to export your code.
exports is an alias to
module.exports, which you can use instead to save yourself a few characters. But be careful: it’s just an alias. Setting individual properties works fine, but if you set the entire
module.exports object (as we do above) you’ll break the connection between the two and it will no longer work.
While there’s nothing wrong with using the exports alias, losing the ablity to set more than one property at a time forces us into some harmful design choices. For that reason along with its fragility, we reccomend sticking with
module.exports. However, if you hate the idea of polluting your namespace with
var self, you can use
exports instead. Just be sure to reset the alias to our new
1 2 3 4
Maintaining the Singularity
After all this, we should probably double-check that we actually have a true singleton. Require is pretty strait-forward, but there is one important thing to remember when using it: If you have the same singleton code in two different files, Node will treat these as two separate modules. If they’re completely stateless this is fine, but if you’re not careful it can cause some major headaches down the road. Make sure you keep your code in a single place so that require can cache it correctly.
Bringing it all Together
To see all these rules come together, check out a complete singleton. But remember, rules are meant to be broken. Use what works, discard what doesn’t, and you’ll be well on your way to Node Nirvana.