I am creating a chat app.
I receive new messages by subscriptions.
I submit new messages by mutations (apollo http).
Intended outcome:
Mutations optimistic updates to be rollback optionally.
I want to let people now the message failed, and later give them the option to "resend" (or even automatically retry to resend when back online, this latter not that important in apollo-http).
Actual outcome:
When apollo http server goes offline (fail for some reason), mutations fail.
Optimistic updates happen but as soon as it realizes the network failed they are rolled back.
Mutation's optimistic updates are rollback by design. So the message vanishes.

How to reproduce the issue:
        optimisticResponse: {
          __typename: 'Mutation',
          messageNew: {
            __typename: 'Message',
            id: -generateId(),
            completed: false,
            createdBy: { 
              __typename: 'User',
              username: myUser.username,
              id: myUser.id
            },
            createdAt: +new Date,
            text
          }
        },  
        updateQueries: {
          MessagesForChannelQuery: (previousResult, args) => {
            return {
              ...previousResult,
              messagesForChannel: [
                ...previousResult.messagesForChannel,
                args.mutationResult.data.messageNew
              ]
            }
          }
        }
                        Hi @gaston23 :wave: I think the right way to solve this would not be to not roll back the optimistic update, but to build retries into the network interface, such that a mutation doesn't fail immediately if the network is down. However, once the mutation has failed, the optimistic update should be rolled back immediately.
PS: I think some people already implemented custom network interfaces or middleware+afterware to retry failed requests. I don't remember where, but it might be worth asking on slack.
@helfer. Good. I will digg more then. Do you think I will be able to mark the "message" as failed? (Add a Boolean to it or something like that) and add a "retry button"? Thanks
@gaston23 the "failed" button could probably be added with a timer of sorts. For example if the message was supposedly sent at 10:33, but it's now 10:35 and it still doesn't have a deliveryTime on it or something. Otherwise we would have to introduce the concept of a kind-of-but-not-quite-failed request, which would make things unnecessarily complicated in my opinion.
@helfer
I understand the idea, still I believe there should be a way to not roll back it but rollback it manually? Why can't this be archived?
I don't really like the idea of timers, it seems like a workaround and so far graphql/apollo has the perfect design to make all this possible. I just think this is a really common use case which needs to be solved in the correct way.
Use case (all mutations with optimistic updates)
- Message A Sent
- Message B Fails. (Retry button)
- Message C Sent
We should discuss the "order" of the messages here, because probably "Message B" after retry works should be moved to the end of the list.
@gaston23 I think manually reverting would require the mutation API to become much more complicated. I'd rather add retries on the network. It doesn't have to be purely based on timeouts, you could also provide a function that decides when to retry.
@helfer great. Would you mind guiding me where to look at to do that? I have read the internals somewhat but it has been only last 3 days using apollo :) Thank you
This functionality would be great for me, too, as I am working on a chat based react-native app and I need to be able to handle connectivity issues. I can technically hack this functionality into application code, but I'd really like to avoid it to be honest.
@helfer I don't think retries will be enough for me. Like with Slack, I need the chat messages to be persisted in memory until the user either cancels the request or kills the app process. Retries won't likely give me enough control.
@gaston23 could you do something as simple as this?
const yourQuery = () {
    apolloClient.query()
     .then( ...something... )
     .catch((err) => {
        displayPopUp('Hey you had an error, click to retry?', yourQuery);
     });
};
could more easily do it on a custom network interface too using an Apollo provided network interface under the hood.
class CustomNetworkInterface {
    constructor() {
        this.networkInterface = createBatchNetworkInterface(...some options...); // use Apollo provided network interface
    }
    query(request) {
        return this.networkInterface
            .query(request)
            .then((data) => {
                if (data.errors) {
                    // loop through errors to find your specific error
                    // retry request
                    // re-calling CustomNetworkInterface.query will lead to an infinite loop,
                    // you may want to modify this so you can set a max # of retries.
                    // This retries just once:
                    this.networkInterface.query(request);
                }
            });
    }
}
                    @fc no I don't think that's enough. The user might want to "cancel" a message that failed to send (just like with Slack). Sticking all of this into a network interface doesn't give enough control to application developers IMO
I agree with Andrew
Best,
Gaston
Sent from my iPhone
On May 17, 2017, at 6:52 PM, Andrew E. Rhyne notifications@github.com wrote:
@fc no I don't think that's enough. The user might want to "cancel" a message that failed to send (just like with Slack). Sticking all of this into a network interface doesn't give enough control to application developers IMO
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
This issue has been automatically marked as stale becuase it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions to Apollo Client!
This issue has been automatically closed because it has not had recent activity after being marked as stale. If you belive this issue is still a problem or should be reopened, please reopen it! Thank you for your contributions to Apollo Client!
I think this still something important to address. Key part of the data design solution Apollo should provide.
@helfer any news on this issue? What is the suggested way to handle mutation errors? Especially with optimisticUpdate provided. It should be a common scenario when you don't want to fully rollback an update on error, but want to partially change it.
I ran into the same issue, I think the best solution is to just roll your own optimistic response handling, it's not so hard, if you already have your own logic for persisting a queue of mutations, to just simply call the mutations update function with the optimistic response manually. If such special handling is needed it's probably easiest to just create custom optimistic response handling.
@helfer This is badly needed. One of the biggest problems I see is how to link mutations with queries. There should be a way to know in a query if it was somehow related to a mutation. And if so, which one, and be able to refetch/retry it. In a chat app, it will be a retry button next to the failed message.
I can only think about adding a directive to a query that somehow locally resolves to the locally mutation id. Then an api to get it.
Would this be possible and a good solution?
A year and months later I had the chance to do this. 99% as I wanted. If someone needs to know how just ping me here, might write a blogpost since it involved many tricks and some patching of Apollo client.
@gastonmorixe Please share the blogpost 😉 I think that would be interesting for all of us here 🙂
I am also interested. maybe we could add callbacks similar to update() specifically for failure? Also we could have an overload for mutate that returns an observable instead of a promise, and have the xhr abort upon unsubscribe? I also think this should be re-opened.
@gastonmorixe I am facing the same issue. please share your solution.
Most helpful comment
I think this still something important to address. Key part of the data design solution Apollo should provide.