Singleton Design Pattern: creating one database instance for your backend application using knex

vitorrafaelm

Vitor Rafael

Posted on June 1, 2023

Singleton Design Pattern: creating one database instance for your backend application using knex

1. Singleton Brief apresentation
The singleton design pattern allow a user to create a class and make sure that exists just one instance of this class in your application and a unique point of access to that instance.

There are some steps to create a singleton class:
1º add a private static field that store the class instance
2º declare a public static method that allow you to access the class
3º Implement lazy initialization inside the public method. It means this method will create an instance of the class if It does not exists yet.
4º Make sure the constructor of the class is private, so the class can be instanciated only inside of itself

2. Context of the implementation
On the implementation I will use the knex query build. Knex is a query builder mainly used on nodejs applications that allows you to create queries and do operations on database, with Knex you can create CRUDs;

With that on mind, I will create just a single class that provides a Knex configuration that you can use in all your application to do operations on the database.

*3. What you need to replicate this? *

  • You will need a nodejs project configured using Fastify. With that, you can create routes to recieve data to store on a database.
  • Also you need to install Knex on the project and configure it. You can see how to to that on:

4. UML Diagram
On this section I want to present you the UML diagram that represents the singletion class I will create:

Image description

5. Let's go to the code

import { knex as setupKnex, Knex } from 'knex'
import path from 'node:path'
import { env } from '../env'

class SetupKnexSingleton {
  private static _instance: SetupKnexSingleton | null = null
  public knex: Knex<any, unknown[]>

  private client: string = 'sqlite'
  private connectionPath: string = path.resolve(__dirname, env.DATABASE_URL)
  private migrationsExtesions = 'ts'
  private migrationsDirectory = './src/database/migrations'
  private useAsDefault: boolean = true

  private constructor() {
    this.knex = setupKnex({
      client: this.client,
      connection: {
        filename: this.connectionPath,
      },
      useNullAsDefault: this.useAsDefault,
      migrations: {
        extension: this.migrationsExtesions,
        directory: this.migrationsDirectory,
      },
    })
  }

  public static getInstance(): SetupKnexSingleton {
    if (SetupKnexSingleton._instance === null) {
      SetupKnexSingleton._instance = new SetupKnexSingleton()
    }

    return SetupKnexSingleton._instance
  }
}

export const knex = SetupKnexSingleton.getInstance().knex
Enter fullscreen mode Exit fullscreen mode

On the class above, you can see we have:
1º private static _instance
2º The constructor is private
3º We have a method getInstance() that returns the instance of the class and inside this method we have the lasy initialization.

With that we have accomplished the use of the Design Pattern Singleton and created and Knex instance that can be used in all the application. The constructor is responsible for creating the Knex configuration.

💖 💪 🙅 🚩
vitorrafaelm
Vitor Rafael

Posted on June 1, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related