In 2020, we released the Transaction API as one of our built-in API services. The purpose of transactions is to protect your data from partial database updates that fail to complete. The Transaction API is now available for Codeless.
We defined the Transaction API in our release article last year:
With the Transaction API, Backendless Database can execute multiple database requests grouped into a single transaction. When any one of these grouped database operations fails, the entire transaction is rolled back – meaning any other changes within the same transaction are canceled.
Until now, the Transaction API was limited to Backendless users building with code. Now, the API is available for Codeless developers as well.
Click here to view the complete Codeless Transaction API documentation.
The following section describes how the Transaction API works and some of its many benefits.
To jump to our Codeless example, click here.
So, what does the transaction API do for you? Let’s look at some of the benefits.
First, if any of the operations fail, the entire transaction is rolled back – there is no need to perform any clean up from your code. In other words, no permanent changes are made to your database that would need to be fixed if an operation fails.
Next, from the client-side perspective, there is only one request going to the server – the request to execute the transaction. For instance, say a transaction performs two operations: (1) saving an object in the database and (2) establishing a relationship between the saved object and others identified with a query. With the Transaction API, the client application sends only one request to the server.
Third, transactions provide consistency and referential integrity for your database. In other words, transactions ensure that there is no “dirty data” in your database. This helps keep your client-side code cleaner and the data consistent.
Backendless transactions rely on the following concepts:
The diagram below illustrates these concepts as they relate to each other:
The heart of the Backendless Transactions API is a request called UnitOfWork
. It is used by client applications to compose a transaction.
Composing a transaction means adding various database operations to a unit of work. The “Find” operation, for retrieve objects from your database, is shown above.
These operations may be “linked” to each other by using the output/result on one operation as the input for another. For example, in the block below, we see an example of an “Update” operation with the option to refer to a previous operation in the transaction.
When the server receives a UnitOfWork
request, it starts processing its operations. The server executes all operations one-by-one in the context of a single database transaction.
If any of the operations fails, Backendless rolls back all the changes and returns the information about the result of each operation to the client. However, if all operations succeed, the entire database transaction is committed. This means the data will be finalized in the database.
Using Backendless Transaction API, you can configure an isolation level for your transaction. The isolation level determines how the Backendless database separates your transaction from all others running at the same time.
Without any isolation, one transaction could modify the data another transaction is reading and, by doing so, data inconsistency would be created.
Isolation levels determine how isolated your transaction is from others. This is implemented by applying a lock on the data retrieved in a transaction.
Different levels of isolation imply different locking mechanisms, each of which also yield various data read phenomena (described further in the documentation). Backendless supports four isolation levels.
One of the most powerful elements of the Transaction API is the ability to use the result of an operation in other subsequent operation(s) within the same transaction. This is made possible by a “protocol” that all transactional operations follow: every operation can have a unique identifier.
The identifier can be used in another operation to reference the result from the operation where it was used. This allows for a “chaining effect” where data comes out of one operation and is then fed into another.
Consider the example below. The example saves a new object in the database and then establishes a relationship between the created object and objects in another table.
The operation to save the object in the database assigns a unique ID to each operation. This ID is then used in the subsequent operation to establish a relationship between the object and its children.
We are going to walk through the following Codeless transaction to break down what is happening in each step. This Codeless block is being used to store details of a purchase. Below, we walk through each step of this API call.
For reference, the table schemas for the two tables in this example are found below. Here’s a breakdown of each step of this API service:
TransactionsDemo
with the Method name createObjectsSaveRelation
. You do not have to create a new API Service to run the logic in this example; all of the following steps can be used in most any Codeless logic you create.myTx
and set its contents to be a new transaction. The isolation for the transaction is set to “Repeatable read”. You can read more about the different transaction isolation levels here.UnitOfWork
) named myTx
. Note: Keep in mind, order matters. Particularly if you have operations that are dependent on another operation in the same transaction, you will need to be sure you put your operation steps in the right order to achieve your desired result.createOrderOp
. This operation creates an object with orderId = 061821-CV1
and amount = 189.2
in the Order
table.createOderItemsOp
. In this operation, we create two data objects – Paper Towels
with quantity = 10
and Bathroom Tissue
with quantity = 20
. These items are added to the table OrderItem
.Order
table. The parent object is then found by accessing the result of the createOrderOp
operation. The column we are creating a relation to is called orderDetails
. To find the children options, we will again access the result of a previous operation. This time, it’s the createOrderItemsOp
operation.The result of this API call will be:
{ "success": true, "error": null, "results": { "set_relationOrder1": { "result": 2, "operationType": "SET_RELATION" }, "createOrderItemsOp": { "result": [ "43925FB1-1F7A-41E6-B8C5-0FB5F8566FE3", "91026D1C-217E-4078-AEDD-2DBDBD10BF7E" ], "operationType": "CREATE_BULK" }, "createOrderOp": { "result": { "amount": 189.2, "orderId": "061821-CV1", "created": 1624004146000, "___class": "Order", "ownerId": null, "updated": null, "objectId": "3A04D2B6-44D3-4A08-AC56-A316366C5069" }, "operationType": "CREATE" } } }
Order table schema:
OrderItem table schema:
Support for transactions in Codeless is just one of the powerful new features in this release. You can read our articles about the other new features released below:
Thanks for reading and Happy Codeless Coding!