112

I recently came across this article on how to write a singleton in Node.js. I know the documentation of require states that:

Modules are cached after the first time they are loaded. Multiple calls to require('foo') may not cause the module code to be executed multiple times.

So it seems that every required module can be easily used as a singleton without the singleton boilerplate-code.

Question:

Does the above article provide a round about solution to creating a singleton?

r0-
  • 2,388
  • 1
  • 24
  • 29
mkoryak
  • 57,086
  • 61
  • 201
  • 257
  • 2
    Here is a 5 min. explanation on this topic (written after v6 and npm3): https://medium.com/@lazlojuly/are-node-js-modules-singletons-764ae97519af – lazlojuly Sep 06 '16 at 08:19

10 Answers10

149

All of the above is overcomplicated. There is a school of thought which says design patterns are showing deficiencies of actual language.

Languages with prototype-based OOP (classless) do not need a singleton pattern at all. You simply create a single(ton) object on the fly and then use it.

As for modules in node, yes, by default they are cached, but it can be tweaked for example if you want hot-loading of module changes.

But yes, if you want to use shared object all over, putting it in a module exports is fine. Just do not complicate it with "singleton pattern", no need for it in JavaScript.

  • 31
    It's weird nobody's getting upvotes... have a +1 for `There is a school of thought which says design patterns are showing deficiencies of actual language.` – Esailija Nov 01 '12 at 14:39
  • 81
    Singletons are not an anti-pattern. – wprl Apr 26 '13 at 14:41
  • @wprl http://programmers.stackexchange.com/questions/40373/so-singletons-are-bad-then-what, read the accepted answer. –  Nov 13 '13 at 15:53
  • 5
    @herby, seems like an overly-specific (and therefore incorrect) definition of the singleton pattern. – wprl Nov 13 '13 at 21:17
  • AFAIK, _that_ is singleton pattern (IOW, the definition is correct). Singleton really is single-global-point accessible single object, most of the time lazy initialized (from wikipedia: "Implementation of a singleton pattern must satisfy the single instance and global access principles"). –  Nov 14 '13 at 13:05
  • 2
    If I'm reading this answer correctly, the only solution to the problem presented is to rely on the load-once feature of modules. I don't understand how a singleton object created "on the fly" in one module otherwise becomes accessible to another module. Or are you saying that the question should not have been phrased using the word "singleton"? If the latter, what term better suits a single resource that is accessible to multiple modules in node.js? I fear I would have asked the question the same way. – Joe Lapp Jun 22 '14 at 20:44
  • 23
    The documentation reads: "Multiple calls to require('foo') **may not** cause the module code to be executed multiple times.". It says "may not", it does not say "will not", so asking how to make sure the module instance is created only once in an application is a valid question from my point of view. – xorcus Dec 24 '14 at 16:29
  • @xorcus: It will, though, if that 'foo' always translates to the same module using same pathname. Which, unless you do some black magic, always does. –  Dec 24 '14 at 19:44
  • 9
    It is misleading that this is the correct answer for this question. As @mike pointed out below, it is possible that a module gets loaded more than once and you have two instances. I'm hitting that issue where I only have one copy of Knockout but two instances are created because the module is loaded twice. – dgaviola Apr 29 '15 at 14:49
  • 1
    @dgaviola node caches modules by full filepath. So if you load the _same_ module twice, you only get one instance. Maybe the issue with you installation is that knockout is npm dependency of two different modules, thus present in two different places. There is a solution for that, though. IIRC: npm 2.0 already reuses top-level dependencies, so you should just include knockout as direct dependency in package.json and reinstall npm deps (rm node_modules && npm install). –  Apr 29 '15 at 20:33
  • The application domain of javascript is increasing. With node js we have full flowing desktop/server side application coming into picture. I don't find anything wrong in using terms which are industry standard for solution/problem definition. – indolentdeveloper Dec 30 '15 at 04:48
  • 2
    This non-answer ignores/mistakes what a singleton is. A singleton is a single and same object (reference) across multiple contexts, which can't be created on the fly by definition. Singletons are useful in a variety of instances, and can be implemented by various means in js, any of which need only ensure that all references to an object are the same. Relying on node's habit of caching old objects is one such example, if not a great idea, the _concept_ is still valid. Other solutions offered here are likely a better idea. – Adam Tolley Aug 08 '17 at 23:46
  • What is “All of the above”? – tim-phillips Sep 25 '21 at 17:08
75

This has basically to do with nodejs caching. Plain and simple.

https://nodejs.org/api/modules.html#modules_caching

(v 6.3.1)

Caching

Modules are cached after the first time they are loaded. 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.

Module Caching Caveats

Modules are cached based on their resolved filename. Since modules may resolve to a different filename based on the location of the calling module (loading from node_modules folders), it is not a guarantee that require('foo') will always return the exact same object, if it would resolve to different files.

Additionally, on case-insensitive file systems or operating systems, different resolved filenames can point to the same file, but the cache will still treat them as different modules and will reload the file multiple times. For example, require('./foo') and require('./FOO') return two different objects, irrespective of whether or not ./foo and ./FOO are the same file.

So in simple terms.

If you want a Singleton; export an object.

If you do not want a Singleton; export a function (and do stuff/return stuff/whatever in that function).

To be VERY clear, if you do this properly it should work, look at https://stackoverflow.com/a/33746703/1137669 (Allen Luce's answer). It explains in code what happens when caching fails due to differently resolved filenames. But if you ALWAYS resolve to the same filename it should work.

Update 2016

creating a true singleton in node.js with es6 symbols Another solution: in this link

Update 2020

This answer refers to CommonJS (Node.js's own way to import/export modules). Node.js will most likely be switching over to ECMAScript Modules: https://nodejs.org/api/esm.html (ECMAScript is the real name of JavaScript if you didn't know)

When migrating to ECMAScript read the following for now: https://nodejs.org/api/esm.html#esm_writing_dual_packages_while_avoiding_or_minimizing_hazards

Kasir Barati
  • 606
  • 13
  • 24
basickarl
  • 37,187
  • 64
  • 214
  • 335
  • 7
    If you want a Singleton; export an object ... that helped thanks – danday74 Aug 05 '17 at 12:43
  • 2
    This is kind of a bad idea - for many reasons given elsewhere on this page - but the _concept_ is essentially valid, which is to say that under the established nominal circumstances, the claims in this answer are true. If you want a quick and dirty singleton, this will likely work - just don't launch any shuttles with the code. – Adam Tolley Aug 08 '17 at 23:51
  • @AdamTolley "for many reasons given elsewhere on this page", are you referring to symlinking files or misspelling file names which apparently do not utilize the same cache? It does state in the documentation the issue regarding case-insensitive file systems or operating systems. Regarding symlinking, you can read more here as it was discussed https://github.com/nodejs/node/issues/3402. Also if you are symlinking files or do not understand your OS and node properly then you shouldn't be anywhere near the aerospace engineering industry ;), I do however understand your point^^. – basickarl Aug 09 '17 at 07:40
  • 2
    @KarlMorrison - just for the fact the documentation does not guarantee it, the fact that it seems to be unspecified behavior, or any other rational reason for not trusting this particular behavior of the language. Maybe the cache works differently in another implementation, or you like working in REPL's and subvert the caching feature altogether. My point is the cache is an implementation detail, and it's use as a singleton equivalent is a clever hack. I love clever hacks, but they should be differentiated, that's all - ( also no one is launching shuttles with node, i was being silly ) – Adam Tolley Aug 09 '17 at 22:06
  • So what if I want a singleton class that actually accepts parameters in the constructor ? I can't just export an object in that case. – Rose Nov 17 '22 at 19:16
31

No. When Node's module caching fails, that singleton pattern fails. I modified the example to run meaningfully on OSX:

var sg = require("./singleton.js");
var sg2 = require("./singleton.js");
sg.add(1, "test");
sg2.add(2, "test2");

console.log(sg.getSocketList(), sg2.getSocketList());

This gives the output the author anticipated:

{ '1': 'test', '2': 'test2' } { '1': 'test', '2': 'test2' }

But a small modification defeats caching. On OSX, do this:

var sg = require("./singleton.js");
var sg2 = require("./SINGLETON.js");
sg.add(1, "test");
sg2.add(2, "test2");

console.log(sg.getSocketList(), sg2.getSocketList());

Or, on Linux:

% ln singleton.js singleton2.js

Then change the sg2 require line to:

var sg2 = require("./singleton2.js");

And bam, the singleton is defeated:

{ '1': 'test' } { '2': 'test2' }

I don't know of an acceptable way to get around this. If you really feel the need to make something singleton-like and are okay with polluting the global namespace (and the many problems that can result), you can change the author's getInstance() and exports lines to:

singleton.getInstance = function(){
  if(global.singleton_instance === undefined)
    global.singleton_instance = new singleton();
  return global.singleton_instance;
}

module.exports = singleton.getInstance();

That said, I've never run into a situation on a production system where I needed to do anything like this. I've also never felt the need to use the singleton pattern in Javascript.

Allen Luce
  • 7,859
  • 3
  • 40
  • 53
  • It’s interesting to read this. However, the final conclusion is: you must __deliberately__ break your code (in this case node caching mechanism) to break singleton pattern in Node.JS! – iaforek Feb 09 '22 at 14:55
  • While I took deliberate steps to demonstrate one way in which singleton assumptions can be violated, there's no guarantee that a similar situation can't arise without the programmer intending it to. – Allen Luce Feb 09 '22 at 17:58
22

Looking a little further at the Module Caching Caveats in the Modules docs:

Modules are cached based on their resolved filename. Since modules may resolve to a different filename based on the location of the calling module (loading from node_modules folders), it is not a guarantee that require('foo') will always return the exact same object, if it would resolve to different files.

So, depending on where you are when you're requiring a module, it's possible to get a different instance of the module.

Sounds like modules are not a simple solution to creating singletons.

Edit: Or maybe they are. Like @mkoryak, I can't come up with a case where a single file might resolve to different filenames (without using symlinks). But (as @JohnnyHK comments), multiple copies of a file in different node_modules directories will each be loaded and stored separately.

dodov
  • 5,206
  • 3
  • 34
  • 65
mike
  • 7,137
  • 2
  • 23
  • 27
  • ok, i read that 3 times and i still cant think on an example where it would resolve to a different filename. help? – mkoryak Nov 01 '12 at 17:17
  • 1
    @mkoryak I think that's referring to cases where you've got two different modules you're requiring from `node_modules` where each depend on the same module, but there are separate copies of that dependent module under the `node_modules` subdirectory of each of the two different modules. – JohnnyHK Nov 01 '12 at 18:33
  • @mike you are right here that module get instantiated multiple times when referenced through different paths. I hit the case when writing unit tests for the server modules. I need kind of singleton instance. how to achieve it? – Sushil Jun 10 '13 at 11:53
  • An example might be relative paths. eg. Given `require('./db')` is in two separate files, the code for the `db` module executes twice – willscripted Dec 28 '13 at 02:37
  • 9
    I just had a nasty bug since the node module system is case-insenstivie. i called `require('../lib/myModule.js');` in one file and `require('../lib/mymodule.js');` in another and it did not deliver the same object. – heyarne Oct 21 '14 at 14:23
  • @mike My question is on statement `.... Since modules may resolve to a different filename based on the location of the calling module.. ` To me this statement means nodejs uses the relative path(instead of absolute path) as cache key as it is dependent o location of calling module . Is that right ? – emilly May 01 '17 at 03:10
19

A singleton in node.js (or in browser JS, for that matter) like that is completely unnecessary.

Since modules are cached and stateful, the example given on the link you provided could easily be rewritten much more simply:

var socketList = {};

exports.add = function (userId, socket) {
    if (!socketList[userId]) {
        socketList[userId] = socket;
    }
};

exports.remove = function (userId) {
    delete socketList[userId];
};

exports.getSocketList = function () {
    return socketList;
};
// or
// exports.socketList = socketList
Paul Armstrong
  • 7,008
  • 1
  • 22
  • 36
  • 5
    Docs say "*may not* cause the module code to be executed multiple times", so it is possible that it will be called multiple times, and if this code is executed again, the socketList will be reset to an empty list – Jonathan. Aug 22 '14 at 18:11
  • 4
    @Jonathan. The context [in the docs](http://nodejs.org/api/modules.html) around that quote seem to make a pretty convincing case that _may not_ is being used in an RFC-style _MUST NOT_. – Michael Mar 02 '15 at 15:32
  • 6
    @Michael "may" is a funny word like that. Fancy having a word that when negated means either "perhaps not" or "definitely not".. – OJFord Aug 13 '15 at 22:48
  • 1
    The `may not` applies when you `npm link` other modules during development. So be careful when using modules that rely on a single instance such as an eventBus. – mediafreakch Feb 04 '16 at 20:00
14

The only answer here that uses ES6 classes

// SummaryModule.js
class Summary {

  init(summary) {
    this.summary = summary
  }

  anotherMethod() {
    // do something
  }
}

module.exports = new Summary()

require this singleton with:

const summary = require('./SummaryModule')
summary.init(true)
summary.anotherMethod()

Only problem here is that you cannot pass params to the class constructor but that can be circumvented by manually calling an init method.

danday74
  • 52,471
  • 49
  • 232
  • 283
  • the question is "are singletons needed", not "how do you write one" – mkoryak Oct 13 '17 at 17:17
  • @danday74 How we can use same instance `summary` in another class, without initializing it again? – M Faisal Hameed Sep 18 '18 at 02:21
  • 1
    In Node.js just require it in another file ... const summary = require('./SummaryModule') ... and it will be the same instance. You can test this by creating a member variable and setting its value in one file that requires it and then getting its value in another file that requires it. It should be the value that was set. – danday74 Sep 18 '18 at 07:28
12

You don't need anything special to do a singleton in js, the code in the article could just as well be:

var socketList = {};

module.exports = {
      add: function() {

      },

      ...
};

Outside node.js (for instance, in browser js), you need to add the wrapper function manually (it is done automatically in node.js):

var singleton = function() {
    var socketList = {};
    return {
        add: function() {},
        ...
    };
}();
Esailija
  • 138,174
  • 23
  • 272
  • 326
7

Singletons are fine in JS, they just don't need to be so verbose.

In node if you need a singleton, for instance to use the same ORM/DB instance across various files in your server layer, you can stuff the reference into a global variable.

Just write a module that creates the global var if it doesn't exist, then returns a reference to that.

@allen-luce had it right with his footnote code example copied here:

singleton.getInstance = function(){
  if(global.singleton_instance === undefined)
    global.singleton_instance = new singleton();
  return global.singleton_instance;
};

module.exports = singleton.getInstance();

but it is important to note that using the new keyword is not required. Any old object, function, iife, etc. will work - there is no OOP voodoo happening here.

bonus points if you closure a some obj inside a function that returns a reference to it, and make that function a global - then even reassignment of the global variable won't clobber the instances already created from it - though this is questionably useful.

Adam Tolley
  • 925
  • 1
  • 12
  • 22
  • you dont need any of that. you can just do `module.exports = new Foo()` because module.exports will not execute again, unless you do something really stupid – mkoryak Jun 07 '16 at 00:46
  • You absolutely should NOT rely on implementation side effects. If you need a single instance just tie it to a global, in case the implementation changes. – Adam Tolley Jun 07 '16 at 16:40
  • The above answer was also a misunderstanding of the original question as 'Should I use singletons in JS or does the language make them unnecessary?', which seems also to be an issue with a lot of the other answers. I stand by my recommendation against using the require implementation as a replacement for a proper, explicit singleton implementation. – Adam Tolley Oct 10 '19 at 19:09
1

Keeping it simple.

foo.js

function foo() {

  bar: {
    doSomething: function(arg, callback) {
      return callback('Echo ' + arg);
    };
  }

  return bar;
};

module.exports = foo();

Then just

var foo = require(__dirname + 'foo');
foo.doSomething('Hello', function(result){ console.log(result); });
mzalazar
  • 6,206
  • 3
  • 34
  • 31
Eat at Joes
  • 4,937
  • 1
  • 40
  • 40
1

If you want to use classes, it's the shortest and most beautiful

module.exports = new class foo {...}
ben
  • 71
  • 1
  • 4