JS/TS: Using the Singleton Design Pattern
Published: 2021-10-16 09:19:19

Classes were added to JavaScript in ES6 as a template for creating and managing objects in your application. They encapsulate data and the code inside each class helps us work with that data.

```js
class Vehicle {
  constructor(speed, weight) {
    this.speed = speed;
    this.weight = weight;
  }
}
```

TypeScript is a superset of JavaScript which primarily provides optional static typing, classes and interfaces. Using TypeScript, you can convert the above example of Vehicle into

```ts
class Vehicle {
  constructor(speed: number, weight: number) {
    this.speed = speed;
    this.weight = weight;
  }
}
```

With that simplistic example, we know what classes are and what they help us with. But, you already knew this.

## So, what are Singleton classes?

Singleton classes are a design pattern, popularly used in Java - and perhaps other languages as well. In object-oriented programming, a singleton class is a class that can have only one object (an instance of the class) at a time and provide your application a global point of access to that instance. Let me give you an example/use-case right after the basic syntax of writing singleton classes

```ts
class MySingletonClass {
  private static INSTANCE: MySingletonClass;
  
  private name: string;
  private constructor() {
    this.name = "noobmaster69";
  }

  public static getInstance(): MySingletonClass {
    if (!this.INSTANCE) {
      this.INSTANCE = new MySingletonClass();
    }
    return this.INSTANCE;
  }

  public getGreetingMessage(): string {
    return `Hello, ${this.name}`;
  }
}
```

Here, `MySingletonClass` is a simple class that follows the singleton pattern with the help of the static members `INSTANCE` and `getInstance`. The `INSTANCE` object is of type `MySingletonClass` because it will can contain an instance of the class. The initiation of the object is done inside the `getInstance` method. This is a personal preference but I like to make my constructors _private_ so new objects of `MySingletonClass` cannot be made outside the scope of the class.

Now that you have created your singleton class, how would you access the methods? You'd do it by

```ts
console.log(MySingletonClass.getInstance().getGreetingMessage());
```

## That's all great, why would I use it?

That's a good question. Classes are essentially used to _create new_ objects, meaning each object will encapsulate different - or even unique - data. Like mentioned at the beginning, singleton classes help us maintain a _single_ instance of an object that can be accessed globally by different parts of your code. The best use-cases for singleton classes would be using them to maintain database services - which is the example I want to use. Here's a look at the code -

```ts
import mysql, { Connection, Query } from 'mysql';
import unnamed from 'named-placeholders';

const toUnnamed = unnamed();

export default class Mysql {
  private connection!: Connection;

  private static INSTANCE: Mysql;

  public static getInstance(): Mysql {
    if (!this.INSTANCE) {
      this.INSTANCE = new Mysql();
    }
    return Mysql.INSTANCE;
  }

  private constructor() {
    this.connection = mysql.createConnection({
      host: process.env.DB_HOST,
      user: process.env.DB_USER,
      password: process.env.DB_PASS,
      database: process.env.DB_DB,
    });

    this.connect();
  }

  connect() {
    this.connection.connect();
  }

  query(sqlString: string, values = []): Promise<Query> {
    return new Promise(async (resolve, reject) => {
      try {
        return this.connection.query(sqlString, values, (err, results) => {
          if (err) {
            return reject(err);
          }
          return resolve(results);
        });
      } catch (error) {
        reject(error);
      }
    });
  }

  namedQuery(sqlString: string, values: {}): Promise<Query> {
    const [query, queryValues] = toUnnamed(sqlString, values);
    return this.query(query, queryValues);
  }

  beginTransaction() {
    return new Promise((resolve, reject) => {
      try {
        this.connection.beginTransaction((err) => {
          if (err) {
            reject(err);
          } else {
            resolve(true);
          }
        });
      } catch (error) {
        reject(error);
      }
    });
  }

  commit() {
    return new Promise((resolve, reject) => {
      try {
        this.connection.commit();
        resolve(true);
      } catch (error) {
        reject(error);
      }
    });
  }

  rollback() {
    return new Promise((resolve, reject) => {
      try {
        this.connection.rollback();
        resolve(true);
      } catch (error) {
        reject(error);
      }
    });
  }

  release() {
    return new Promise((resolve, reject) => {
      try {
        if (this.connection) {
          this.connection.end();
        }
        resolve(true);
      } catch (error) {
        reject(error);
      }
    });
  }
}
```

If you were to use the above snippet for maintaining a connection to your MySQL database, your entire application would have access to the singular connection/instance that is required throughout the lifetime of your application, i.e. you won't have to create new objects each time you want to run MySQL queries in your application. To give you an example, if you wanted to run a MySQL query, you'd simple use

```ts
Mysql.getInstance().query(myQuery);
```

And, that's it! That's singleton classes for you - cleaner, meaner, and makes code maintenance simpler.

If you liked what you read, leave a <3 and a comment! :)