NodeJs Singleton Injector
Stefanos Kouroupis

Stefanos Kouroupis @elasticrash

About: Studied Surveying Engineering and became a Software Engineer.

Location:
Leicester
Joined:
Apr 14, 2019

NodeJs Singleton Injector

Publish Date: Jun 21 '19
14 2

I am going to talk about one of my most used patterns.

When given the task to write an API using NodeJs and Express I like to implement the following pattern, which I tend to call Singleton Injector (yes I like to make up names) that allows me to manage singletons easily.

This pattern is ofcourse the singleton pattern with a small mixture of (but not quite) the dependency injection pattern.

My application entry point looks as follows

(async () => {
    await addAndInitDepedencies();
    // express stuff. configure and start server
})();

async function addAndInitDepedencies() {
    injector.addService(MySql, DatabaseConnection.MySql);
    const mysql = injector.getService<MySql>(MySql);
    mysql.init();

    // other singletons that follow the same pattern
}
Enter fullscreen mode Exit fullscreen mode

So when I need to use for example the mysql connection pool, I can simply do

 this.mysql = Injector.getInstance().getService<MySql>(MySql);
 this.mysql.executeQuery(sql, []);
Enter fullscreen mode Exit fullscreen mode

and this infamous Singleton Injector is just the following really simple class

export class Injector {
    private static instance: Injector;
    private _di: any = {};

    private constructor() { }

    public static getInstance() {
        if (!Injector.instance) {
            Injector.instance = new Injector();
        }
        return Injector.instance;
    }

    public addService(dependency, args) {
        if (this.serviceExists(dependency.name)) { return; }
        const construction = new dependency.prototype.constructor(args);
        this._di[dependency.name] = construction;
    }

    public getInjector() {
        return this._di;
    }

    public getService<T>(dependency): T {
        if (!this.serviceExists(dependency.name)) { return {} as T; }
        const di: any = this._di as T;
        return this._di[dependency.name];
    }

    private serviceExists(name: string) {
        return this._di[name] ? true : false;
    }
}

Enter fullscreen mode Exit fullscreen mode

basically you provide the class you wish to use as well as any arguments that might need, it calls its constructor and returns back the initialized object. And you can get any singleton back anytime by using the class as a type and parameter.

Nothing fancy, just small stuff that makes your life easier.

To be completely honest, until two years ago, I only used Node to write command line tools (so much better than bash), as my background was mainly in c# and c++. It's been an interested trip.

Comments 2 total

  • Andrei Gatej
    Andrei GatejJun 26, 2019

    Interesting!

    You can also get a singleton just by exporting a new instance of a class from a file.

    Say you want to have a singleton of a DB class, in your db/index.js you would have the following:

    class Database {
      static connection;
      /* ... */
     }
    
    export default new Database(dbConfig);
    

    And now you could import this in multiple files and you will get the same instance.

    This is possible due to how modules are designed to work in node. Long story short, the modules are cached.

    • Stefanos Kouroupis
      Stefanos KouroupisJun 26, 2019

      Yes I've seen it been done like that before. But I always found it a bit awkward (syntactically). I ll be the first to admit that this idea in some respects it's equally awkward...but why not.

      I choose to do it like that because we usually have lots of things to manage in a single service.

      A typical service will have the following

      Rabbitmq
      Database
      Vault
      Elasticsearch

Add comment