valerioneri
valerioneri

Reputation: 57

Mutation based on previous value with Graphql and Amplify/AppSync

I wanna point that I am new to Graphql and I am probably missing something obvious. I would like to do something similar to a shop's order system. When a customer buys something, two things are done: (1) The money is decreased from his wallet and (2) an order is added to his order history. As of now, I modelled the wallet and the order history as two types in the GraphQL schema.

Point 1. Ideally, I want to have a transaction. If either the wallet update or the order creation fails, the whole should fail. From what I understood, to archive this it will suffice to have a single mutation that does both the updates, as pointed in this answer. So, have I understood it correctly and something like this works like a transaction (rolling back in case of failure)?

mutation {
   update_wallet(...) {...}
   add_to_history(...) {...}
}

Point 2. Secondly, I would like to store the average quantity that each customer bought. To do this, I was thinking to use the incremental average function (this one), so I was thinking to store the current average and the update it with the formula. But, I do not understand how to do it "safely" (without possible race conditions). As far I understood, there is an option for an atomic increment in GraphQL, but that's it. So I thought I could store both the sum_of_all_the_items and the number_of_purchases in order to compute the division per-request. Which could be fine, even if it does not seem the cleanest solution. Particularly because those numbers will grow indefinitely and eventually (even if it is a very unlucky scenario) overflow.

Thank you

Upvotes: 2

Views: 1174

Answers (1)

Hung Tran
Hung Tran

Reputation: 1645

It's a question for the database behind your AppSync. It's maybe DynamoDb, or RDS. Typically, you need only one mutation which performs a customer purchase, including

  1. Deduct money from the wallet
  2. Deduct a number of items from the inventory
  3. Add/Update an order record (orderId, userId, numberOfItems, status)
  4. Add a purchase record (orderId, purchaseId, more purchase info)

All of them are performed in a single transaction. If your DB is an RDS, then you can easily figure out the average number of items by a SQL query on the Order table. The number is aggregated in real-time, and there is no race condition.

On the other hand, if it's DynamoDb, there are many options in this answer. Race conditions in DynamoDb can be a different problem, and one solution I know is conditional update.

One mutation should be enough for your case. The mutation purchaseWithoutOrder is for purchases without an order.

type Mutation {
  purchase(orderId: ID!): PurchaseResult
  purchaseWithoutOrder(input: PurchaseWithoutOrderInput!): PurchaseResult
}

type PurchaseWithoutOrderInput {
  items: [Item!]!
}

Upvotes: 3

Related Questions