How (not) to create a singleton in Node.js

This morning a colleague asked me a question about exporting a module in Node.js and how to export a singleton and I had trouble coming up with an answer that covered the different ways you can create a module in Node.js, and we know what that means.

If you can’t explain it simply,
you don’t understand it well enough.

Albert Einstein

From looking at Stack Overflow which referenced the Node.js documentation it rapidly became apparent the real question was actually how to avoid creating something that behaves like a singleton.

The Node.js documentation states:

This means (among other things) that every call to require(‘foo’) will get exactly the same object returned, if it would resolve to the same file.

Multiple calls to require(‘foo’) may not cause the module code to be executed multiple times. This is an important feature. With it, “partially done” objects can be returned, thus allowing transitive dependencies to be loaded even when they would cause cycles.

If you want to have a module execute code multiple times, then export a function, and call that function.

It’s important to note that this applies to require statements that resolve to same file anywhere in your application.

Typical module declaration

If you want to create something that behaves like a singleton, you might normally do something like this in your module:

var HelloWorld = (function () {  this.greeting = “Hello, World”;  return this;})();module.exports = HelloWorld;

This would mean that if you load the module multiple times in your application, even in different files, HelloWorld will be invoked only once; and all instances of HelloWorld will point to the same object.

var helloWorldA = require(‘./hello-world’);
var helloWorldB = require(‘./hello-world’);
// Change the value of 'greeting' from "Hello, World" to "Hi"
helloWorldA.greeting = "Hi";
// Outputs "Hi" twice

Avoid singleton behaviour by returning a module as a function then executing it

If you are trying to avoid this behaviour — perhaps because you are tracking the state of something within a module — you can instead declare your module like this:

function HelloWorld() {  this.greeting = “Hello, World”;  return this;}module.exports = HelloWorld;

And include it by specifying the new keyword after requiring it.

var helloWorld = new (require(‘./hello-world’))();

Note the parenthesis matter; you wouldn’t want to do “new require(‘./hello-world-function’)” as that would just create a new require object, not a new instance of the object you are requiring.

If you did that your code would execute but it would actually be returning something that behaved like a singleton and not creating a new instance.

An alternative, easier to read syntax for doing the same thing is:

var HelloWorldModule = require(‘./hello-world’),
helloWorld = new HelloWorldModule();

Example usage and output

In the following example helloWorldA and helloWorldB behave like singletons while helloWorldC and helloWorldD behave like unique instances of the module.

// Create singletons
var helloWorldA = require(‘./hello-world’)(),
helloWorldB = require(‘./hello-world’)();
// Create new instances of the object
var helloWorldC = new (require(‘./hello-world’)),
helloWorldD = new (require(‘./hello-world’));
// Change the greeting property in helloWorldA and helloWorldB
helloWorldA.greeting = “Hi”;
// Change the greeting property in the helloWorldC only
helloWorldC.greeting = “Hello again”;

The output from the above code is:

Hello again
Hello, World

What’s going on here is helloWorldA and helloWorldB actually just referrer to the same cached instance of the hello-world module, while helloWorldC and helloWorldD are completely seperate objects.

How not to not create a singleton

The magical singleton behaviour is actually caused by require caching objects.

As some Stack Overflow threads get into it’s possible to access the require cache and invalidate it as an alternative way to cause code in modules to be executed every time they are invoked — but messing with cache invalidation rarely ends well.

There are only two hard things in Computer Science:
cache invalidation and naming things.

Phil Karlton

Software for news and media and civic tech. Cat herder. Director at Glitch Digital.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store