Apollo-client: refetchQueries after mutation

Created on 14 Jul 2017  ·  49Comments  ·  Source: apollographql/apollo-client

Intended outcome:

I'm trying to refetch some queries after the mutation and I expect them to be executed.

Actual outcome:

refetchQueries are not working as they are described in the docs

How to reproduce the issue:

mutate({
  mutation: LOGIN_MUTATION,
  variables: {
    ...values
  },
  refetchQueries: [USER_QUERY]
})

LOGIN_MUTATION and USER_QUERY are strings in graphql syntax. I don't see USER_QUERY gets executed after the mutation.

Most helpful comment

@tokenvolt I think this may be a shape issue within refetchQueries. Can you try

refetchQueries: [
{ query: USER_QUERY }
]

The object is needed in order to support things like variable changes

All 49 comments

From the documentation:

A list of query names which will be refetched once this mutation has

Also you will have to convert your LOGIN_MUTATION graphql strings to a GraphQL AST by using graphql-tag

It should look like this:

import gql from 'graphql-tag'

const USER_QUERY = gql`
  query currentUser {
    id
    login
  }
`

const LOGIN_MUTATION = gql`
  mutation login {
    login(login: "foobars")
  }
`
[...]

mutate({
  mutation: LOGIN_MUTATION,
  variables: {
    ...values
  },
  refetchQueries: [`currentUser`]
})


I'm using graphql-loader and writing mutations and queries in separate graphql files. How should I approach this way? BTW I've tried to use gql and use the name of the query, but it doesn't work as well.

then you should probably create a reproduction with https://github.com/apollographql/react-apollo-error-template . I never used refetchQueries so my knowledge is limited to the documentation 😞

See also: https://github.com/apollographql/apollo-client/issues/1697 where I tried to figure out the same problem. Did not find a really good solution yet but it may give you some insight in the options.

@tokenvolt I think this may be a shape issue within refetchQueries. Can you try

refetchQueries: [
{ query: USER_QUERY }
]

The object is needed in order to support things like variable changes

@jbaxleyiii still no luck

@tokenvolt do you think you could create a simple reproduction for this? We have an error template and code sandbox listed in the issue template (try to create a new issue and it should be there) or a failing test?

@jbaxleyiii I'll try to reproduce

@tokenvolt FYI I was also not lucky with using the query-name based variant of refetchQueries. While it _should_ work as expected there currently seems to be a race-condition which effectively prevents it from working, see https://github.com/apollographql/apollo-client/issues/1821

In principle though, the query-name based refetchQueries is an expected feature, here's the implementation:

Hey, guys, I figured out that it was an issue in my application. Sorry for bothering, as it was just a stupid mistake on my side. refetchQueries works as it was mentioned in https://github.com/apollographql/apollo-client/issues/1900#issuecomment-315765415.

But I am too experiencing a race condition https://github.com/apollographql/apollo-client/issues/1821

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!

For those who are still having this problem, try putting refetchQueries inside options:

name: '...',
options: {
  refetchQueries: [USER_QUERY]
}

@abraga still doesn't work for me

Hey I'm experiencing same problme, any solutions?? none of the things mentioned above solved the issue. Everything is working fine, but refetchQueries

Same issue here - I can manually call .refetch(), but this doesn't seem to trigger

@wesbos Hi Wes how you doing? I had to use a workaround here, I imagine you are using a state container(Redux, Mobx), so you have the records collection in your store and every time you make a mutation, select all the objects fields, once the response comeback you can use that data to merge it into your existing records collection. Do you catch the idea?

I'm not using any sort of state container outside of Apollo - so I'm not sure this applies

I was having a similar issue trying to refetch a query after my login mutation. But I placed some console.logs and it looks like in my issue it may just be an async issue. My mutation is firing first but the query is firing before my result gets back so it makes the query i am firing appear to have no change in my UI. I tried simply wrapping my resolver in async/await but of course it was not that easy :P

So did you figure it out?

@wesbos My query is taking my userId through apollo's context object. I believe that the query is firing before I can set the context so it still sees context.userId as undefined and so my query appears to be the same as when my user was logged out. If I run the query again it updates as it is supposed to because now that my user is logged in the context is set. I am in the middle of trying to figure it out but I believe apollo-link-context is what I am looking for but no I have not solved it yet.

I am not sure if you are using context or not but my refetch works fine otherwise so I would say this may be an async issue for you as well but I am in the same boat as you so who knows :P

So I ended up solving my issue for the most part. I needed to wrap my login resolver to return a Promise. What was happening is my context was being updated before I received my login token. If your refetch queries is firing before your login process is finished than the query will appear to not change. I am not sure if this helps but I wanted to share what I found.

works well when you use optimisticResponse :(

after a while, i used the reply of @abraga like a started point, and search in other issiues and found that, the way that i be able to solve this problem was

the code that @abraga supply was

name: '...',
options: {
   refetchQueries: [USER_QUERY]
}

Instead, I used:

name: '...',
options: {
   refetchQueries: [{query: USER_QUERY}]
}

I'm using "withApollo" HOC and graphql as an decorator.

Guys, just a heads-up:

When using React, if you call this.props.mutate({ options: {refetchQueries: [ 'queryToRefetch']}}) it doesn't work. However, if you pass the options parameter to the graphql function it works.

...

export default graphql( my_mutation, { name: 'my_mutation', options: {refetchQueries: [ 'queryToRefetch']} } ) ( MyComponent)

Use a Map to pass the limitation

https://github.com/apollographql/react-apollo/blob/4285679f59af2c5b9e6ba0076c9aaabe30f50a2f/src/Mutation.tsx#L196

mutate({
  .........,
  refetchQueries: new Map([[0,'todolist'],[1, 'todogroups']])
})

function bypass(...queryNames){
  return new Map(queryNames.map((name, i) => [i, name])
}

mutate({
  .........,
  refetchQueries: bypass('todolist', 'todogroups')
})

I'm having an issue re-rendering the component.

1- I have an index showing basically all my bank accounts (it uses bank account query)
2- I have a modal, which is a form and adds a new bank account. When the modal is open, the index is in the back.

  1. When the mutation adding a new bank account is done, refetchQueries is called (I can see in the network tab the query is done) and also the store is updated (I can see so looking at the apollo devtools)
  2. Even though the store is updated and the query is executed, the "index" of bank accounts is not updated. I'm not sure what could be the problem.

@n1mmy

A list of query names which will be refetched once this mutation has

This is absolutely not obvious what is expected.
As @tokenvolt I also provided a list of gql(...) tags and not what you write.

Their documentation is miserable.

@abraga

try putting refetchQueries inside options:

What options are you talking about? The options you provide are the only options. You can't have options.options.
Read https://www.apollographql.com/docs/react/api/apollo-client.html#ApolloClient.mutate

How can you specify options here?

    this.props.client.mutate({
      mutation: CreatePatientMutation,
      refetchQueries: ['getPatientDetailsQuery'],
      options: ???? // HOW?
      variables: {
        foo: 'bar'
      }})
        .then(res => {
        })
        .catch(err => {
        });

Experiencing a similar situation as @waclock. I see that new data is being fetched, but the component depending on that query doesn't update.

Hi guys!
I solved this problem with the following code:

<Mutation
  mutation={REMOVE_USER}
  variables={{ userId: user.id }}
  refetchQueries={[{ query: GET_USERS }]}  // <--- HERE
>
  {removeUser => (
    <button onClick={removeUser}>Remove User</button>
  )}
</Mutation>

Hope it helps.

FYI if the solution @slorenzo posted isn't working try this

So far there's been like 8 different methods posted, most of which have never worked for me. It's completely ridiculous that it's such a game of whispers (does it change in every version?).

FWIW, in my case I was using compose to add my mutations to props, so I'm going:

this.props.upsertPerson({
    variables {
          payload: {
             ...fields
         }
    },
    refetchQueries: () => [{ query: GET_PERSON, variables: {id: personId} }]
})

That works for me. None of the other posted solutions did anything.

This is complete nonsense and yes @MitchEff 's solution works:

await client.mutate({
        mutation: COMPLETE_CHALLENGE,
        variables: data,
        refetchQueries: () => [{query: UNCOMPLETED_CHALLENGES}]
})

I get error back of You must wrap the query string in a "gql" tag. when I add the query name.

refreshQuery = [{ query: ['GetCollectionByUserId'], variables: { userId : userId, first : 100, last : 0, cursor : "" }, }]

adding the query itself does nothing.

How do I wrap the query in gql statement if it's a query name?

The query is stored in a file and imported into a constant on first run.

ok so passing the entire query seems to be accepted (no error) but also my data is still old. is there another promise I need to process on to wait for the second query to process before I pass to he result to the calling method?

query: gql`${CollectionsByUserID}`,

right now I do mutate {blah blah).then(stuff) is there now a new var passed to then to tell me when the refresh has occurred or will it only pass me stuff for the then after it's mutated AND refreshed the cache. Thanks for any help.

Update: it seems to work after the second call (the first call looks like nothing happens, but click update again and it works fine.) Any advice , as it always seems one record behind.?

Solved: had to add awaitRefetchQueries = true to my options array.

This worked for me. Thanks to @slorenzo 's solution

client.mutate({ mutation, variables, refetchQueries: [ { query: ...name, variables: { ...queryVariables } } ] })

Any idea how do we get the response data from refetchQueries? I got the query response data from mutation.

Mutation
import { gql } from 'apollo-boost';

export const DONE_TASK = gql`
    mutation DoneTask($taskId: ID!) {
        doneTask(input: {
            taskId: $taskId
        }) {
            task {
                id
                status
            }
        }
    }
`;
Query
import { gql } from 'apollo-boost';

export const GET_TASKS_BY_STATUS = gql`
    query GetTasksByStatus($status: String!) {
        getTasksByStatus(status: $status) {
            edges {
                node {
                    id
                    status
                    description
                }
            }
        }
    }
`;
Usage
const response = await client.mutate({
    mutation: DONE_TASK,
    variables: {
        taskId: 1
    },
    refetchQueries: () => [{
        query: GET_TASKS_BY_STATUS,
        variables: { 
            status: "OPEN"
        },
    }]
});

console.log(response);
Output
data: {
    doneTask: {
        task: { id:  1, status: 'DONE'}
    }
}

But I expect a response data from GET_TASKS_BY_STATUS. 🤔 😕

Still experiencing this issue. I've tried the solutions listed with no success. Will be diving into the race issue listed as I have yet to explore that potential.

Here is where I'm at with no luck:

// NOTHING UPDATES on session update
function App({ apolloClient }) {
    const { loading, error, data } = useQuery(GET_CURRENT_SESSION);

    if (loading) return null;
    if (error) console.log('error', error);
    const { session } = data;
    console.log('app session', session);  // NO UPDATE

    return (
        <Router history={history}>
            ...
            <Authentication/>
            ...
        </Router>
    );
}

// UPDATES on session.isLoading update
function Authentication(props) {
    ....
    updateSessionLoading({ 
        variables: { loading: true },
        options: {
            refetchQueries: () => [{ query: GET_CURRENT_SESSION }],
            awaitRefetchQueries: true
         }
    });
    ....
    return (
        ...
    )
}

Maybe this helps:

When I'm loading the query from a .graphql file via graphql.macro the refetch doesn't work.
But when I'm declaring it with gql, it works just fine.

const [mutate] = useMutation(ASSIGN_QUESTIONNAIRE_FOR_USER, {
    variables: { userId: clientId },
    refetchQueries: ['questionnaires'],
    awaitRefetchQueries: true,
})

await mutate({
    variables: { templateId: id },
})
export const QUESTIONNAIRES_LIST = gql`
  query questionnaires($userId: ID) {
    questionnaires(userId: $userId) {
      id
      type
      date
      finished
      user {
        firstName
        lastName
      }
    }
  }
`

I wanted to recreate it with codesandbox but I can't find any public graphQL API, where I can write data.

This is complete nonsense and yes @MitchEff 's solution works:

await client.mutate({
        mutation: COMPLETE_CHALLENGE,
        variables: data,
        refetchQueries: () => [{query: UNCOMPLETED_CHALLENGES}]
})

How did you get "refetchQueries" response

I've been wrestling with getting the Apollo client to wait for the queries specified in refetchQueries to resolve before resolving the mutation promise and wanted to chip in with something I just learned in case it helps anyone still struggling with this.

For one, you need the awaitRefetchQueries option to be set to true on the mutation. But even with that set, it seems that the fetchPolicy of the watched query that you're refetching makes a difference if you're specifying those refetchQueries by strings.

For example, given a mutation a call like this:

await client.mutate({
  mutation: MUTATION,
  variables: data,
  refetchQueries: ['QueryName'],
  awaitRefetchQueries: true
})

The client.mutate promise will resolve too soon if that query was called with the cache-and-network fetch policy. But if that query uses cache-first or network-only, I'm seeing that the mutation promise waits until the refetch query resolves, as expected.

@tokenvolt I think this may be a shape issue within refetchQueries. Can you try

refetchQueries: [
{ query: USER_QUERY }
]

The object is needed in order to support things like variable changes

worked.

My solution to refetch using variables on Apollo 3.0:

import { gql, useApolloClient } from "@apollo/client";

const client = useApolloClient();

const SEARCH_PROJECTS = gql``

const removeProject = async () => {
         // Your mutation or query here

         const projects = (await client.query({
                    query: SEARCH_PROJECTS,
                    variables: { page: 1, limit: 1 },
                    notifyOnNetworkStatusChange: true,
                    fetchPolicy: "network-only"
          })).data.searchProject
}

See more about the fetch policy here.

This is complete nonsense and yes @MitchEff 's solution works:

await client.mutate({
        mutation: COMPLETE_CHALLENGE,
        variables: data,
        refetchQueries: () => [{query: UNCOMPLETED_CHALLENGES}]
})

How did you get "refetchQueries" response

Hi @hbw3 , sorry for the big delay, but i hope this post helps other people that have the same question as you.

All refetch queries are managed as normal queries, so we have access to all methods as if we are calling a individual one. You can check this in the documetation.

So, a example for this is:

useMutation(MUTATION_GQL, {
    variables: {
        id: variable_mutation,
    },
    refetchQueries: [
        {
            query: QUERY_1,
            variables: {
                id: variable1,
                some: variable2,
            },
            fetchPolicy: 'network-only',
            onCompleted: data => {
               console.log(data)
            },
            onError: data => {
               console.error(data)
            },
        },
        {
            query: QUERY_2,
            variables: {
                id: variable3,
            },
        },
    ],

});

As u can see, in onCompleted we have access to the result of refetchQuery and we can use this to do whatever we want. Same with the onError function.
For more information about the accepted params you can check this section

If anyone runs into this same issue, what fixed it for me since I've never had any issues with refetchQueries before, was this:

I had a loop over a child collection, and was then fetching the parent in a refetchQueries when a child was deleted. Setting the id or the child as key somehow made a difference... rule.id below

```javascript
{data.rules_RuleSet &&
data.rules_RuleSet.rules.map((rule, i) => )}

The thing that fixed it for me was changing the fetchPolicy on the awaiting component. So that it didn't default to using the cache

const { data, error } = useGetProductsQuery({
fetchPolicy: 'cache-and-network'
});

Was this page helpful?
0 / 5 - 0 ratings