How to Code a Singleton in Node.js

URL copied to clipboard
By AstroMacGuffin dated  last updated 
![laptop-g276260f56_1280.jpg](/static/img/mm/coding/laptop-g276260f56_1280.jpg) This will be a short article because it's an easy topic. Node.js caches your modules when you `require()` them, but there's a caveat: if there's a change in the path to the file being required, it gets treated as a separate module, with a separate cache entry. As long as you code around that hiccup, you'll be slinging singletons in no time. But first, why are singletons even useful? Imagine you have a game engine. Modern games live and die on events, and your code has to register events in such a way as to ensure all the events are in the same registry. If they aren't, then your event chains won't work: event A is supposed to trigger event B, but if events A and B are registered in two separate objects, that chain of events won't happen. Of course, doing things the normal way, you'd have this at the top of your file: ```js const gameEventManager = require('./path/GameEventManager'); ``` That module naturally ends with this: ```js module.exports = new GameEventManager(); ``` ...and then whenever you need to use the `gameEventManager` you'd do this: ```js gameEventManager.registerEvent( theEvent ); ``` We'll be making some slight modifications to get a Node singleton.

### Ingredients - a module that exports an object (not a class, an object) - an API function - find & replace ### The Problem You're requiring `GameEventManager.js` from multiple files. Those files exist in different folders. Because of that, the `require()` path for `GameEventManager` changes. Your main game loop and every class file in your project certainly don't exist in the same folder all together. So you have a base class in `c/b/SomeClass.js` which needs the game event manager, but you also need it in your main loop in `c/mainloop.js`. This means two different `require()` paths: `from SomeClass.js:` ```js const gameEventManager = require('../s/GameEventManager'); ``` `from mainloop.js:` ```js const gameEventManager = require('./s/GameEventManager'); ``` Oh what a difference a single dot can make! According to Node's module loader, those are two different paths, and so you will have two different `GameEventManager` objects running in your app. Your game events won't inter-operate. You need a true singleton. ### The Solution You need to centralize your `gameEventManager` object -- you need to ensure only one copy of that object is running around in your code -- you need a singleton. And it's easy to accomplish. #### Step 1: Create the API You should have a central API file for misc high-level functions in every project. Let's pretend it's in the project root folder and it's called `api.js`. Edit that file, and add something like this at the top where you include your library files: ```js const gameEventManager = require('./c/s/GameEventManager'); ``` Then you need a function to return the `gameEventManager` object, i.e. in the same file: ```js function getGameEventManager() { return gameEventManager; } ``` And finally you need to export that function as part of your module. There are multiple ways for a module to export a function, I prefer `module.exports` at the bottom of the file: ```js module.exports = { doSomething, doSomethingElse, getGameEventManager }; ``` #### Step 2: Use the API Now you just need to go through your project files doing some replacements. Every file that has this: ```js const gameEventManager = require('./path/to/GameEventManager'); ``` ...replace it with this: ```js const {getGameEventManager} = require('./path/to/api'); ``` And secondly, everywhere that you actually use the `gameEventManager` object, you need to replace that as well. For example if you called the `registerGameEvent` method like this: ```js gameEventManager.registerGameEvent( theEvent ); ``` ...you now need to call that method like this: ```js getGameEventManager().registerGameEvent( theEvent ); ``` If your editor has project-wide find and replace, you can speed things along: find `gameEventManager.` and replace it with `getGameEventManager().` for a good time. #### Step 3: Done! That's it! Now that the module is only being loaded from one place, and since the module creates and exports an object instead of a class, you now have a Node.js singleton!
Profile image

About the author of this piece, AstroMacGuffin: "I enjoy coding in Node.js and JavaScript as a hobby. I find Node.js to be a welcome escape, after a lifetime of code, with previous experience with PHP and JavaScript on the LAMP stack. I'm one of those people who spends way too much time herding electric sheep in front of a computer. You can find me on the JavaScript Mastery discord, where I'm a moderator, or on the Classic Shadowrun discord, where I'm the owner and founder. "

For more by AstroMacGuffin, click here.


Valid HTML!Valid CSS!Powered by Node.js!Powered by Express.js!Powered by MongoDB!