Build a Reliable Node.js API with TypeORM using Transactions

Build a Reliable Node.js API with TypeORM using Transactions
Photo by Caspar Camille Rubin on Unsplash

Build a Reliable Node API with TypeORM using Transactions

Javascript and NodeJS have one of the most popular ecosystems for the development of modern web applications. It contains a plethora of useful resources and frameworks that facilitate part of the work for us.

One of the major concerns today while building an API that is gonna be publicly available is to ensure that the service provided is reliable and secure. Today we are gonna see how database transactions are a great way to improve the quality of an API service and avoid a variety of issues in your application.

Example of an Online Shop

Let’s consider a simple Node Express API that we have developed for managing some online shops that we run.

In this example, we are going to deploy a PostgreSQL database via a docker container.

The API was setup with TypeScript and the TypeORM framework in order to interact with the database.

API Endpoints

In order to allow the customer to interact with our application, the API exposes the following endpoints at the /order router:

  • Open Order
  • Update Order
  • Complete Order
  • Get Order

Then we can open an order with a post request at the /order endpoint specifying the shopId and the desired products and their quantities:

{ 
"shopId": 1,
"productsRelation": [
{ "productId": 1, "quantity": 2},
{ "productId": 2, "quantity": 1}
],
"clientEmail": "test@example.com"
}

In this simple API, we are not going to take into account all the security features which should come with a real e-commerce server, as we try to make it as simple as possible. With the open order, the user is able to update it, add or remove products as it wishes.

At the end of this process, the order can be confirmed at the checkout process via the /order/complete route. Which is going to be handled by an specific method at the OrderService class:

We are considering that the completeOrder route is going to be responsible at the checkout process for:

  • Validating the order payment, for simplicity we were just going to assume it will be registered in our database
  • Sending an email notification to the user and registering it in our database
  • Updating the order entry at our database

Our API is not very reliable

Normally, the method above would work without any problem while processing any customer’s order. However, we have to take into account the possibility of something going wrong in the middle of this process.

What if there is an error at the moment the customer was supposed to be notified via its e-mail? It means that the checkout process would fail right in the middle of it, and we just completed a part of the entire operation!

The payment has been validated, but there was no e-mail notification, nor the order status was updated. We end up with our database at the middle of a checkout process.

How can we solve it?

At first, we can think of solving this issue by first updating the order entry at the database and then notifying the costumer e-mail that the order was completed. While it may work sometimes, this is not a consistent solution. What if we have to introduce another step at the checkout process? It might be not as simple as in this case.

This is where database transactions come into play. A transaction is just a block of all-or-nothing operations. Either all of them run smoothly, either one of them fails and every modification made is rolled back to the DB before the transaction.

This concept is widely used for database systems. With PostgreSQL, the commands to start and to finish a transaction are START and COMMIT:

START;
UPDATE product SET stock_quantity = 0 WHERE id = 2;
UPDATE order SET order_status = 'awaiting_delivery' WHERE id = 1;
INSERT into email VALUES ('customer@email.com', 'Thank you for your purchase');
COMMIT;

If we change our minds about any of our changes, we can just run the ROLLBACK command instead of COMMIT. You can check out more about them in the official documentation.

Using transactions with TypeORM

Fortunately, TypeORM is a pretty complete framework, which also supports transactions. Following the official documentation, they can be set up in some different ways.

In this article I am going to show you the one that I have adopted in my projects. Which is by wrapping the entire business logic inside a helper called transactionContext:

The input is a function that receives as an argument a TypeORM EntityManager, and is responsible for executing the entire business logic.

There will be two possible outcomes:

  • Either the transaction wrapper will run successfully the serviceMethod input, returning its output and committing the transaction
  • Either an error will be thrown inside serviceMethod and the entire transaction will be rolled back to its state before the start of the transaction.

Now we can apply our wrapper to our route controllers and solve our problem with the checkout route:

Every interaction with the DB will be done through the transactionManager, it can be injected wherever we need to run any query.

Going back to the completeOrder method, we have:

Where we are injecting the transaction manager wherever we want to communicate with the underlying DB. Now if there’s an error anywhere in our code, it will roll back any modification made to the database before throwing an error.

The entire API code is available in this git repository

From now on, you are going to be able to build much more reliable APIs and services with Node and TypeORM. Also, I would like to make it clear that this is only of the possible approaches of handling database transactions with TypeORM, you can refer to the official documentation in order to learn more about how they can be applied.

Thanks for you attention and making it to the end!

Bit: Feel the power of component-driven dev

Say hey to Bit. It’s the #1 tool for component-driven app development.

With Bit, you can create any part of your app as a “component” that’s composable and reusable. You and your team can share a toolbox of components to build more apps faster and consistently together.

  • Create and compose “app building blocks”: UI elements, full features, pages, applications, serverless, or micro-services. With any JS stack.
  • Easily share, and reuse components as a team.
  • Quickly update components across projects.
  • Make hard things simple: Monorepos, design systems & micro-frontends.

Try Bit open-source and free→

Learn more


Build a Reliable Node.js API with TypeORM using Transactions was originally published in Bits and Pieces on Medium, where people are continuing the conversation by highlighting and responding to this story.

Leave a Reply

Your email address will not be published.