NodeJs Singleton Injector

Stefanos Kouroupis - Jun 21 '19 - - Dev Community

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.

. . . . . . . . . . . . . . . . . . . . . . . . .