Nest JS TypeORM Transaction

·

3 min read

Nest JS TypeORM Transactions

Lets me first talk about TypeORM and Nestjs

typeorm.io TypeORM is an ORM that can run in NodeJS, Browser, Cordova, PhoneGap, Ionic, React Native, NativeScript, Expo, and Electron platforms and can be used with TypeScript and JavaScript (ES5, ES6, ES7, ES8). Its goal is to always support the latest JavaScript features and provide additional features that help you to develop any kind of application that uses databases - from small applications with a few tables to large scale enterprise applications with multiple databases.

nestjs.com est (NestJS) is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with and fully supports TypeScript (yet still enables developers to code in pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).

In my past videos and Blogs i have already covered how we can setup typeORM with nestjs projects, In this Blog we will tak about transations with typeORM

What is Database Transactions (some already answered here)

A transaction is a way of representing a state change. Transactions ideally have four properties, commonly known as ACID: stackoverflow.com/questions/974596/what-is-..

  • Atomic (if the change is committed, it happens in one fell swoop; you can never see "half a change")
  • Consistent (the change can only happen if the new state of the system will be valid; any attempt to commit an invalid change will fail, leaving the system in its previous valid state)
  • Isolated (no-one else sees any part of the transaction until it's committed)
  • Durable (once the change has happened - if the system says the transaction has been committed, the client doesn't need to worry about "flushing" the system to make the change "stick")

Whatever we do in Database must follow ACID principles, Here the question is how should we manage transactions using TypeORM so that we don't break ACID principles simple example for transactions

import {getConnection} from "typeorm";

// get a connection and create a new query runner
const connection = getConnection();
const queryRunner = connection.createQueryRunner();

// establish real database connection using our new query runner
await queryRunner.connect();

// now we can execute any queries on a query runner, for example:
await queryRunner.query("SELECT * FROM users");

// we can also access entity manager that works with connection created by a query runner:
const users = await queryRunner.manager.find(User);

// lets now open a new transaction:
await queryRunner.startTransaction();

try {

    // execute some operations on this transaction:
    await queryRunner.manager.save(user1);
    await queryRunner.manager.save(user2);
    await queryRunner.manager.save(photos);

    // commit transaction now:
    await queryRunner.commitTransaction();

} catch (err) {

    // since we have errors let's rollback changes we made
    await queryRunner.rollbackTransaction();

} finally {

    // you need to release query runner which is manually created:
    await queryRunner.release();
}

There are 3 methods to control transactions in QueryRunner:

  • startTransaction - starts a new transaction inside the query runner instance.
  • commitTransaction - commits all changes made using the query runner instance.
  • rollbackTransaction - rolls all changes made using the query runner instance back.
    const queryRunner = this.connection.createQueryRunner();
    try {
      await queryRunner.connect();
      await queryRunner.startTransaction();

      const data =  queryRunner.manager.find(Entity,{ where : { id : 'UUID' , status: In([ RequestStatus.INPROGRESS,RequestStatus.NOTSTARTED ])} })
      await queryRunner.manager.update(
        Entity,
        { id },
        {
         name: data.name
        },
      );
      await queryRunner.commitTransaction();
      return data;
    } catch (err) {
      await queryRunner.rollbackTransaction();
      throw err;
    } finally {
      await queryRunner.release();
    }

another example

async createMultipleBooks(books: Book[]) {
   const queryRunner = this.connection.createQueryRunner();

        await queryRunner.connect();
        await queryRunner.startTransaction();

        try {
            await queryRunner.manager.save(books[0]);
            await queryRunner.manager.save(books[1]);

            await queryRunner.commitTransaction();
        }catch (err) {
            await queryRunner.rollbackTransaction();
        }finally {
            await queryRunner.release();
        }
}

conclusion

Soon i will post this in Video also showing how to manage complex transactions using typeORM library, I hope these examples are helpful to understand how to manage DB transactions

References