Course contentsShow
← Node.js
Lesson 170 of 1,9802. Module Systems and Environment ConfigurationFree lesson

Global Singleton Management in ESM

Safely manage shared state across module boundaries in an ESM-first architecture.

In this lesson, you will learn how to maintain a single, shared instance of an object across your entire Node.js application using ECMAScript Modules (ESM).

The Challenge of State

In a modular architecture, you often need a centralized service—like a database connection pool or a configuration manager—that maintains its own internal state. If you aren't careful, you might accidentally create multiple instances of this service as different files import it, leading to inconsistent data and memory leaks.

Leveraging ESM Caching

The beauty of ESM in Node.js is that modules are cached by default. When you import a module for the first time, Node.js executes the code and stores the resulting exported object. Every subsequent import of that same file returns the exact same reference. We can use this behavior to create a "Singleton"—a pattern where only one instance of a class ever exists.

Consider this DatabaseConnector.js module:

class DatabaseConnector {
  constructor() {
    this.connectionString = "db://production-cluster";
    console.log("Initializing database connection...");
  }

  query(sql) {
    return `Executing ${sql} on ${this.connectionString}`;
  }
}

// We instantiate it right here before exporting
const instance = new DatabaseConnector();

// Export the singleton instance
export default instance;

Why This Works

Because DatabaseConnector.js is an ESM file, Node.js runs the code at the top level only once. When you import db from './DatabaseConnector.js' in your app.js and again in auth.js, both files receive the exact same instance created during the first load.

Because the class is instantiated inside the module scope, the instance is protected from external tampering. You don't need to manually check if an instance exists (as was common in older patterns); the module system handles the uniqueness for you. This keeps your shared state predictable, memory-efficient, and easy to debug across your dependency tree.

Key Takeaway: By instantiating a class within the module scope and exporting that specific instance, you leverage the ESM cache to guarantee that your service remains a true global singleton across your entire application.