BUG REPORT
What is the current behavior?
My mutation's update callback is being ran twice, adding it twice to the view. When you refresh the page, the duplicate item is gone. I initally thought it was adding it to local storage twice, but this is not the case. Items get added once to local storage but twice to my component data.
Steps to recreate
I've been following this guide to create a simple API. I created a simple schema using the default wizard, (called it posts), then exported it to my application. I just added one extra query (that I don't use right now) and that's all I did. My code can be found here at line 61
What is the expected behavior?
When using a mutation to create a new post, it should only create 1 new item in my UI. However the mutation update() is being called twice. At first I thought this was related to defining an optimistic response, but this behavior still occurs when I comment out that property.
The only "fix" for this I've found is by adding disableOffline: true to my AppSync client config. However this is not a great fix since it removes offline capabilities.
Which versions and which environment (browser, react-native, nodejs) / OS are affected by this issue? Did this work in previous versions?
Windows 10, Chrome, VueJS. No idea if it worked previously.
After some testing, it appears that my update property of the mutation is being called twice. This isn't normal, right? I apologize as I am new to GraphQL and AppSync in general.
I can confirm.
example below:
update={(cache, { data }) => {
const { addSkill: addedSkill } = data;
const { getSkills } = cache.readQuery({ query: GET_SKILLS })
cache.writeQuery({
query: GET_SKILLS,
data: {
getSkills: getSkills.concat([addedSkill])
}
})
}}
This adds the addedSkill to getSkills (skill list) twice. However, in by database it is only added once.
Versions:
"aws-appsync": "^1.4.0",
"aws-appsync-react": "^1.2.0",
Unlike @tqhoughton disabling offline did NOT work for me. I am also using apollo-link and the second argument of the AWSAppSyncClient constructor.
With further investigation in my fork ( just forked an hour ago, so not experienced in this codebase ):
on line 225 of client.ts if I set doIt to true, it works as expected with only 1 update. Not saying that be a fix, but just thought I would throw this out there in case it helps with development. I will continue to post new findings.
Can someone change this back to a bug report instead of a question?
I even cloned this repo and set everything up and in this project I'm getting 2 update() calls. I'm guessing this project used to work normally, but it doesn't anymore.
I tried reverting my AppSync version to 1.0.10 but it had no effect. Would this imply this is some issue on the AppSync back end?
Hi, this is not an AppSync specific behavior but how the Apollo client works with optimistic response. The first update callback is from the Apollo cache when the optimistic response is returned and the second is the actual network response. It's independent of the AppSync client (which caches the responses to persistent storage, handles auth, and subscription handshakes) and default Apollo client behavior. You can read more about this in the official Apollo documentation:
The second argument to the update function is an object with a data property containing your mutation result. If you specify an optimistic response, your update function will be called twice: once with your optimistic result, and another time with your actual result. You can use your mutation result to update the cache with cache.writeQuery.
ref: https://www.apollographql.com/docs/react/essentials/mutations.html#update
@undefobj Then why does this happen even with no OptimisticResponse defined?
The repo that you linked to uses an optimistic response: https://github.com/dabit3/vue-graphql-appsync/blob/master/src/components/Tasks.vue#L42
If you've modified that or have a separate project could you post a gist for troubleshooting?
@undefobj I have created a jist here of the files at play in my project.
Note: This does not occur when disableOffline: true
https://gist.github.com/xSequential/37341972ccb9346ff2abddfb1edf1d18
For the console.log you can see the optimisticResponse is ran once.
It actually is running through the update function a total of 3 times. Once for the Optimistic Response and twice for the server response. It does throw away the optimistic response properly as far as I can tell. In my situation it is simply running the server response twice.
Please apprise if you need more, and thank you.
@undefobj I did some testing last night and for some reason that project does work correctly when you take out the optimistic response. I'll have to dig deep and figure out what the differences are between that project and my own.
@tqhoughton ok sounds good. If you want to send me a gist I can look at it.
@xSequential I was actually replying to @tqhoughton who is the original poster on this thread with my comments earlier. To address your questions I looked at your code and one thing I noticed is this line of code:
data: {
getSkills: getSkills.concat([addedSkill]),
},
This will duplicate data in the cache, you need to filter with something like:
https://github.com/awslabs/aws-mobile-appsync-sdk-js/blob/7a3f5cec214ea4a7c64a41c9106967e2e2fad0fc/packages/aws-appsync/src/helpers/offline.ts#L154
Or like this:
https://github.com/aws-samples/aws-mobile-appsync-events-starter-react/blob/master/src/Components/NewEvent.js#L116
We really recommend that you use the Offline Helpers if possible because they do all these common cache operations for you. Take a look here: https://github.com/awslabs/aws-mobile-appsync-sdk-js#offline-helpers
@undefobj Thanks for the reply.
This fixed my issue with the duplicate item. Appreciate your help.
@xSequential @tqhoughton would something in our documentation have been helpful here, rather than going to the Apollo docs? We do link to them and also have the offline helpers. At the risk of duplicating their docs would having something in ours around how Apollo works in this sense have helped you?
I had subscribed to this thread because I had noticed this behavior too. I agree that with an optimistic response the update function should be called twice, but what is actually happening is that the update function is being called three times (once for the optimistic response and then twice for the server response).
While creating an "idempotent" update statement of sorts does work around the behavior, I am curious to know why the update is executed a total of 3 times, rather than twice (once for optimistic and once for server). This doesn't seem to be the intended behavior, as the apollo docs that were quoted before indicate that the update should only be called once for the optimistic response and once for the server response.
@ack210 it depends on the number of mutations in the offline queue as they are executed in serial
@undefobj Just a thought on documentation. Apologies if this was already made and I have not found it.
I know you do not want to word for word copy the Apollo docs ( who would ). But maybe a doc that explicitly lays out key differences between the two. I have not used Apollo in a larger project, but in a situation like this, I was confused as well because I read the same line @ack210 read from the apollo docs while working on my mutation and was confused to see 3 update invocations when only 2 were expected per Apollo docs.
The second argument to the update function is an object with a data property containing your mutation result. If you specify an optimistic response, your update function will be called twice: once with your optimistic result, and another time with your actual result. You can use your mutation result to update the cache with cache.writeQuery.
If a "pure" Apollo client would do the same, then I would say Apollo needs to update their docs to dig into the why their may be 3 update invocations in that same section. However, if that is not the case, and Apollo would only have 2, then I would suggest some sort of diff doc.
We just released a new centralized documentation on the Amplify Framework site which includes a "How it works" section outlining this process which I hope helps the architecture and understanding: https://aws-amplify.github.io/docs/js/api#client-architecture
@tqhoughton separate to the explanation of the process has your original issue been resolved?
Hi @tqhoughton just wondering why did you close this? Is this bug fixed?
I figure I must have done something wrong in my application, but I didn't have the time to fix it. I ended up copying someone else's project and it's behaving correctly now. I must have done something wrong in my initial test project and couldn't find what I did wrong.
I am also experiencing mutation update functions being run twice (with no optimistic response) with disableOffline: false and only once (as is documented) with disableOffline: true.
Is this intended behaviour? Does offline support somehow require that the update function be invoked twice instead of once?
Experiencing same behavior. It's a problem for me because my custom resolver updates multiple tables and using optimistic response + update method runs three times, 1 for optimistic response, 2 for update. For example, I need to inc/dec a value in store along with the expected data but since optimistic response gets rolled back once real data comes, I can't do it there. And if I inc/dec when the real data comes, it does it twice.
Same problem here.
I'm getting this issue as well, update is getting run twice with no optimistic response
@cocacrave It may or may not be an issue but with your approach you will likely end up with corrupt data since you can't guarantee that certain operations are run on the client.
Most helpful comment
Can someone change this back to a bug report instead of a question?