Newsletter
Newsletter

Dynamically Managing Database Schemas with NestJS and Knex.js

Scroll down
Emad Khan
Emad Khan
.๐๐ž๐ญ ๐‚๐จ๐ซ๐ž | ๐€๐ง๐ ๐ฎ๐ฅ๐š๐ซ | ๐๐ฎ๐ฑ๐ญ๐‰๐’ | ๐๐ž๐ฑ๐ญ๐‰๐’ | ๐๐ž๐ฌ๐ญ๐‰๐’ | ๐‚๐ซ๐š๐Ÿ๐ญ ๐’๐œ๐š๐ฅ๐š๐›๐ฅ๐ž & ๐„๐ง๐ญ๐ž๐ซ๐ฉ๐ซ๐ข๐ฌ๐ž ๐‹๐ž๐ฏ๐ž๐ฅ ๐€๐ฉ๐ฉ๐ฌ๐Ÿ†
  • Mobile
    +923452362514
  • Residence
    Pakistan

May 20, 2023

14:32

admin

In the world of backend development, managing database schemas can be a challenge. If youโ€™re building complex applications that require changes to the database structure over time, you might find yourself manually creating and updating database tables. This article presents a more efficient approach.

Letโ€™s take a deep dive into a code snippet that leverages the power of NestJS, a progressive Node.js framework for building efficient and scalable server-side applications, and Knex.js, a SQL query builder that is designed to be flexible, portable, and fun to use.

The provided solution allows us to create or update database tables dynamically, driven by request payloads. This will greatly enhance our ability to manage our database schema in a more automated and flexible manner. Letโ€™s break down the code into smaller snippets and understand what each part does.

import { Body, Controller, Post } from '@nestjs/common'; 
import { KnexSchemaBuilderService } from './knex.service'; 
 
@Controller('knex') 
export class KnexController { 
  constructor(private readonly knexService: KnexSchemaBuilderService) {} 
 
  @Post() 
  async createOrUpdateTable(@Body() body): Promise<string> { 
    const { tableName, schema } = body; 
 
    if (!tableName || !schema) { 
      throw new Error('Both tableName and schema are required'); 
    } 
 
    await this.knexService.createOrUpdateTable(tableName, schema); 
 
    return `Table ${tableName} created or updated successfully`; 
  } 
}


Theย KnexControllerย class handles the incoming HTTP POST requests. Theย createOrUpdateTable()ย method is decorated with theย @Post()ย decorator and is responsible for creating or updating the database table. The method takes the incoming request’s body as an argument, where we expect to receive theย tableNameย andย schema. If any of these are missing, it will throw an error. Upon receiving valid data, it calls theย createOrUpdateTable()ย method from theย KnexSchemaBuilderService.

import { Module } from '@nestjs/common';
import { KnexController } from './knex.controller';
import { KnexSchemaBuilderService } from './knex.service';

@Module({
  imports: [],
  controllers: [KnexController],
  providers: [KnexSchemaBuilderService],
})
export class KnexSchemaModule {}

Here, we define the KnexSchemaModule that encapsulates the KnexController and KnexSchemaBuilderService for dependency injection by NestJS.

import { Injectable } from '@nestjs/common';
import { Knex, knex } from 'knex';

@Injectable()
export class KnexSchemaBuilderService {
  private knex: Knex;
  constructor() {
    this.knex = knex({
      client: 'mssql',
      connection: {
        host: 'localhost',
        user: 'xxxxx',
        password: 'xxxx',
        database: 'xxxxx',
      },
    });
  }
  // rest of the class
}

The KnexSchemaBuilderService is the core of our code. This service creates an instance of Knex, configured for an MSSQL database with the provided connection parameters.

async createOrUpdateTable(tableName: string, schema: any): Promise<void> {
  if (await this.knex.schema.hasTable(tableName)) {
    await this.updateTable(tableName, schema);
  } else {
    await this.createTable(tableName, schema);
  }
}

This method is the serviceโ€™s main entry point, checking if the table exists in the database. If it does, it will update it, and if not, it will create it.

private async createTable(tableName: string, schema: any): Promise<void> {
  await this.knex.schema.createTable(tableName, (table) => {
    table.increments('id').primary();

    for (const key in schema) {
      const type = schema[key];
      this.addColumn(table, key, type);
    }
  });
}

This method will create a new table based on the provided schema. It automatically adds an โ€˜idโ€™ column as the primary key.

private async updateTable(tableName: string, schema: any): Promise<void> {
  const existingColumns = await this.knex(tableName).columnInfo();

  await this.knex.schema.alterTable(tableName, (table) => {
    for (const key in schema) {
      const type = schema[key];
      if (!existingColumns[key]) {
        this.addColumn(table, key, type);
      }
    }

    for (const key in existingColumns) {
      if (!schema[key] && key !== 'id') {
        table.dropColumn(key);
      }
    }
  });
}

The updateTable() method will check the existing columns in the table and alter the table according to the provided schema. It will add any new columns from the schema that are not present in the table. It will also remove any columns that exist in the table but are not present in the new schema, with the exception of the ‘id’ column.

In conclusion, this NestJS module combined with Knex.js service allows us to create and update SQL database schemas dynamically, providing a powerful tool for managing evolving database structures. Remember to always handle schema changes with care, as they can result in data loss if not managed properly.

Posted in TechnologyTags:
1 Comment
  • Derick

    This design is incredible! You most certainly know how to keep a reader amused.
    Between your wit and your videos, I was almost moved to start my own blog (well, almost…HaHa!) Excellent job.
    I really enjoyed what you had to say, and more than that, how
    you presented it. Too cool!

    Also visit my blog post :: best gold ira companies

    11:30 am December 30, 2023 Reply
Write a comment
ยฉ 2024 All Rights Reserved.
Write me a message

    * I promise the confidentiality of your personal information