Implementing transaction processing in flutter using AWS amplify
Introduction
Implement transaction processing with AWS amplify, which provides the same functionality as firebase. Implementing transactions with amplify is quite a bewildering experience for app developers who have been using firebase. In firebase, transaction processing can be easily implemented using the Firestore Sdk, but amplify requires a wide range of knowledge. First of all, AWS services such as AppSync, DynamoDB, and CloudFormation are involved (of course, Cognito, an authentication service, is also required). And what we will implement is that we need to write instructions for executing graphql queries in the flutter app, and write the definition of transactional queries in schema.graphql. We need to write a resolver in VTL (apache velocity template language) to convert graphQl requests and responses into requests and responses to DynamoDB, and we need to write json (yaml) to deploy the resolver to CloudFormation. The following is a summary.
AWS services to be used to implement the transaction(There are other services that we are directly aware of.)
Implementations required to realize transactions
Instructions for executing a graphQl query
Define transactional queries in schema.graphql
Resolvers in VTL (apache velocity template language)
Json(yaml) for deploying the resolver to CloudFormation
If you’re an app engineer switching from firebase, this will all be new to you. I was. I stumble across everything about the implementation that is required to implement a transaction. You need to grant access to DynamoDB using IAM, and you can view the contents of the table in the DynamoDB console, but there is a trap that the table is not reflected in real time. I know that app engineers have a lot of trouble, so I’d like to share some of the stumbling blocks I’ve experienced and their solutions so that you can have a little less trouble.
Scenario
Let’s consider the following scenario for transaction implementation. The transaction reduces the inventory of the product and creates the order information. The number of items in stock and the number of orders need to be created consistently. The two tables to be used are Products and Orders.
Steps
Proceed with the following steps.
※Assuming you have already completed the initial setup and authentication for AWS amplify.
- Create a table:The output is a schema.graphql
- Create a mutation for transaction:The output is a schema.graphql
- Create a resolver for request:The output is a Mutation.xxxxxx.req.vtl
- Create a resolver for response:The output is a Mutation.xxxxx.res.vtl
- Create a yaml for for deploying:The output is a xxxxxxxxxx.json
- Deploy
- Check behavior with AppSync
- Implement transactions in flutter app
- Check behavior with transactions in flutter app
Performance
Let’s check out the procedure.
1. Create a table:The output is a schema.graphql
Open /amplify/backend/api/[your app name]/schema.grapql. Describe the table definitions for the Products Table and Orders Table.
L1 : In graphQl, you can write type to make it a table definition.
L1 : The @model is a graphQl directive provided by amplify. This will create a table in DynamoDB.
L2 : “!” means non-null.
L2 : The ID type is a String type that means unique.
After deploying the schema.grapql, you will be able to refer to it in the DynamoDB console.
2. Create a mutation for transaction:The output is a schema.graphql
Open /amplify/backend/api/[your app name]/schema.grapql. Create a Mutation. Mutation in graphQl means create, update, and delete. This Mutation is called by the Flutter App.
L1 : Define the type of theMutation argument in input type.
L7 : Define the type of the return value of the Mutation, since the return value of a transaction with create and update will always be “keys” : [{“id” : String!}].
L15 : Creates the Mutation. Specify the types of arguments and return values.
After deployment, the Mutation createOrderTransaction looks like this in the AppSync console. You can test the Mutation createOrderTransaction in the AppSync console before integrating it into your Flutter app.
3. Create a resolver for request:The output is a Mutation.xxxxxx.req.vtl
Implement transaction requests using the VTL (apache velocity template language). Create the file Mutation.[your mutation name].req.vtl in /amplify/backend/api/[your app name]/resolvers/. This time the file name will be Mutation.createOrderTransaction.req.vtl. This transaction checks out that the inventory count has not changed in the Products Table, and then subtracts the inventory count if it has not changed. If the Products Table is updated successfully, a new item will be created in the Orders Table. If the inventory count changes, or if the update process fails, the transaction will be terminated without doing anything to the Products Table and Orders Table.
L2 : #set stores the value in a variable.
L2 : The $ signifies a variable.
L6 : You can accept the Mutation createOrderTransaction arguments in $ctx.args.input.
L7 : $util is a utility provided by AppSync.
L7 : The $util.qr() is used when using the method. This is apparently to prevent garbage data from being output to the template.
L9 : Create a Map with put.
L10, L11 : Specify the update contents.
L13 : “N” means that it is a number.
L13 : ${} will output the variable as a string.
L12 : The “S” means that it is a letter.
L25 : Specify the target table, which can be found in the DynamoDB console: Products-???????????? -dev part.
L26 : Specify the operatin. To update, use UpdateItem.
L43 : Specify the operatin. To create, it is PutItem.
L54 : Specify the vertion, which should be fixed on 2018–05–29.
L55 : Specifies the operatin. Transactions with changes are fixed to TransactionWriteItems.
4. Create a resolver for response:The output is a Mutation.xxxxx.res.vtl
Implement transaction responses using the VTL (apache velocity template language). Create the file Mutation.[your mutation name].res.vtl in /amplify/backend/api/[your app name]/resolvers/. This time the file name will be Mutation.createOrderTransaction.res.vtl. I think you can copy and use the following code.
The resolver uses AppSync to convert the graphql requests into something that DynamoDB can understand. There are many resolvers stored under /amplify/#current-cloud-backend/api/[your app name]/build/resolvers. These are the resolvers that amplify automatically generates for create, update, delete, get, etc. by adding @model to type in schema.graphql. These can be helpful when creating a custom resolver.
5. Create a yaml for for deploying:The output is a xxxxxxxxxx.json
Create CreateOrderTransactionResources.json under /amplify/backend/api/[your app name]/stacks/. The name is optional. Use this json to give CloudFormation the instructions to deploy the resolver. This code can be used by copying and pasting except for the following.
L30: Describes the resolver name.
L36: The name of the table to operate on. In schema.graphql, add ‘Table’ to the name given by the @model directive. Be careful here. The tables we want to manipulate in the transaction are OrdersTable and ProductsTable. However, only one of them could be described here(Maybe I just don’t know. If you run a transaction with this, you will get an authentication error. In the code below, you have specified OrdersTable, so after deployment, the IAM role will be given access to OrdersTable. However, you also need to have access to the ProductsTable. In the IAM console, add ProductsTable to the policy for this role. This will allow the Mutation to access OrdersTablet and ProductsTable.
L37,L54,L78: Describe the Mutation name implemented in schema.graphql.
After deployment, you can check it in the CloudFormation console.
6. Deploy
Run amplify push. After deployment, you can see the results in the AppSync, DynamoDB, and CloudFormation consoles.
7. Check behavior with AppSync
You can check the behavior of transactions with AppSync. The arguments are specified in the central window. The window on the right is the execution result. You can see that the id of the updated Products item and the id of the added Orders item are returned.
8. Implement transactions in a flutter app
Let’s call the transaction implemented in the flutter app. Use the amplify api package to execute the query. Run the query two times in a row to see the effect of the transaction.
L3: Create a query.
L15: This is the first execution of the query.
L19: This is the second execution of the query.
L23: This is the first execution result.
L28: This is the second execution result.
9. Check behavior with transactions in a flutter app
Let’s check the log to see the results of the transaction execution: the first one succeeded and the second one failed. You can see that the transaction worked as expected.
L2: This is the result of the first transaction execution. You can see that the id of the updated Products item and the id of the added Orders item are returned.
L3: This is the result of the first transaction execution. The error is empty.
L5: This is the result of the second transaction execution, where the id of Products and Orders are blank.
L6: This is the result of the second transaction execution. You can see that an error has occurred.
L7: You can see that the transaction has been canceled.
Summary
In this article, we have implemented transaction processing with AWS amplify. Compared to firebase, I felt that the barriers to implementation were higher as it required a variety of knowledge. For flutter engineers, if you have no experience with graphql or vtl, implementing transactions in amplify may not be technically or mentally challenging. Since there are probably not that many implementations of transactions in one application, you would be even more hesitant in that case. That is why I think it is very meaningful to read through this article, as it will give you a complete picture of the transaction implementation. You will find firebase too convenient and amplify too inconvenient. Currently, AWS has a large share of the market (especially for major system development in Japan), and some clients often specify AWS instead of GCP. In Japan, AWS’s track record and strong security are highly regarded. When AWS is specified, it is natural to use amplify instead of firebase. It’s no longer inevitable as a flutter engineer. Amplify is a pain to implement, but if you know how to use it, you can do a lot more with it than with firebase. You can also use amplify to become a flutter engineer at a higher level.