Is It Always The Same Instance (Singleton)?
Is logger always the same instance? Is it a singleton? Do NodeJS and frontend behave in the same way?
Context
We are developing a web application in NodeJS and want to add error monitoring. We decide on a library that requires us to create an instance and export it. We can also add user data to that same instance once the user logs in to provide more context for the error.
After adding the library, we have something like the following:
// logger.js
import { Logger } from “error-monitoring”;
const logger = new Logger(<some-api-key>);
export default logger;
Then we import it and set the user in another file.
// login.js
import logger from “<path-to>/logger”;
// user logs in
logger.addUser(<user-data>);
// …
Then whenever we want to log an error:
// file1.js
import logger from “<path-to>/logger”;
// in a catch somewhere
logger.push({ err: “<some-error>” });
This means that our file structure is something like the following:
Is
logger
always the same instance? Is it a singleton?
What If It’s Not a Singleton?
If logger
is not always the same instance, push
will not have the information we added in addUser
. That’s because logger.push
and logger.addUser
would not share the same data.
Therefore, it’s essential that our logger library always uses the same instance (or a singleton).
NodeJS Modules
To find out, we add a console.log(“in da logger.js”);
in the file where we initialize the logger. If we get multiple logs, it means that the file is executed multiple times and logger
is not a singleton.
We run the code with console.log
and find out it’s printed only once. Perfect logger
is a singleton.
Why is that?
The answer is in the NodeJS docs:
“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.” NodeJS Docs on Modules
Disable Caching
To prove the module caching, we disable caching and check that the log is printed multiple times. For example, in one of the files where it’s imported, we need to add the following:
// some-file.js
import logger from “<path-to>/logger”;
// new line
delete require.cache[require.resolve('<path-to>/logger')];
// …
The new line removes the module from the cache and forces the file to be executed again. Consequently, the instance is not a singleton any more.
Frontend
That was in NodeJS, but is it also a singleton if the project is a front-end project?
Front-end projects are built with bundlers like Webpack or Rollup. Therefore, we need to understand what they do.
I created a simple project in Rollup to show what happens there.
Does It Import the Instance Twice?
The answer is no. The file that Rollup bundles contains the logger file only once. The final file is just a concatenation of all the project files.
// bundle.js
'use strict';
// code from logger.js
console.log('in da logger');
class Logger {}
const logger = new Logger();
// code from file1.js
console.log('in da file1', logger);
// code from login.js
console.log('in da login', logger);
// code from index.js
console.log('in da index.js');
Webpack does the same thing, yet in a much more complicated file.
Is It a Singleton?
The answer is YES. The logger
is a singleton. We can rely on the push
to have the data from addUser
because both are the same instance.
If you like this post, consider sharing it with your friends on twitter or forwarding this email to them 🙈
Don't hesitate to reach out to me if you have any questions or see an error. I highly appreciate it.
And thanks to Michal, Miquel, and Sebastià for reviewing this article 🙏