How to tell ApolloClient InMemoryCache not cache null?

My following query

query getMyAccount {
  myAccount {
    ...Account
  }
}

Might return null. Since I use the default cache-first fetch policy, it will keep returning null even if it has been updated elsewhere.

Hey @yuki :wave:

Just to clarify, are you saying that when myAccount is null, then something else writes a non-null value to the cache, your query is not updating with the new value? It should definitely update with the new value. Would you be able to provide some more code on your queries? It’s difficult to tell from the query alone what might be happening.

Sorry for the late reply, I wrote some test code to explain what I encountered:
GraphQL server side:

extend type Query {
  book(bookId: String!): Book
}

extend type Mutation {
  updateBook(bookId: String!, name: String!): Book!
}

type Book {
  _id: String!
  name: String!
}

GraphQL client side:

query getBook($bookId: String!) {
  book(bookId: $bookId) {
    _id
    name
  }
}

mutation updateBook($bookId: String!, $name: String!) {
  updateBook(bookId: $bookId, name: $name) {
    _id
    name
  }
}

client-side code (Note: I wrap around @apollo/client/core myself)

  ngOnInit(): void {
    this.courseApi.getBookWatchQuery({ bookId: 'test' }).valueChanges.subscribe();
    setTimeout(() => {
      this.courseApi.updateBook({ bookId: 'test', name: 'Hello World!' });
    }, 3e3);
  }

Apollo Chrome extension capture (New users can’t post picture…):

It seems the whole query is used as the cache ID even if the returned value is null, so that watch query didn’t emit new value.

I want to do two things in different use cases:

  1. If the returned value is null, just ignore it, don’t cache it.
  2. Let book({"bookId":"test"}):null reference Book:test even if it’s value is null.

@yuki so the problem you’re experiencing is that Apollo Client doesn’t know your schema, so its unaware that the book(bookId: ID!) field returns a Book type and that its value should be associated to the book you updated from your mutation. Since its null at first, it has no way to know these two objects should be related. You need to give it a little help to understand that the book you updated in the mutation should now be the book associated to that book field in the query.

I’m not super familiar with the Angular semantics, but the core mutate function gives you access to an update callback. Use this to update your cache after the mutation so that you can associate the two. It should look something like the following:

apolloClient.mutate({
  mutation: gql`...`,
  update(cache, { data }) {
    // If we didn't get a book back, don't do anything
    if (!data.updateBook) {
      return
    }

    cache.writeQuery({
      query: gql`
        query ($bookId: ID!) {
          book(bookId: $bookId) {
            _id
            name
          }
        }
      `,
      variables: { bookId: data.updateBook._id },
      data: data.updateBook,
    });
  },
});

Let me know if that helps!

1 Like

@jerelmiller Thank you! But it works partically.

Let’s say I have following GrahQL schema:

query getBook($_id: String!) {
  book(_id: $_id) {
    _id
    name
  }
}

mutation updateBook($_id: String!, $name: String!) {
  updateBook(_id: $_id, name: $name) {
    _id
    name
  }
}

query getBooks {
  books {
    _id
    name
  }
}

Now, not only updateBook but also getBooks can update getBook. What if I came up more and more operations which can update getBook.

Is there a way to make an association on getBook side? I mean I want to associate book({"_id":"test"}) with Book:test even if it got null, so that later mutations or querys can always update it automatically.

Ah thats a great question! You can use something we like to call cache redirects which are essentially just read functions that give a hint to the cache where something should be looked up. Try defining one of those to see if that helps here!

Query: {
  fields: {
    book: {
      read: (_, { args, toReference }) => {
        // note the `args` key will match the argument name in the schema, 
        // not the variable name
        return toReference({ __typename: "Book", _id: args._id })
      }
    }
  }
}

Let me know if this works for you!

@jerelmiller This works! But I found a bug in Apollo Chrome extension:

The value in code gets updated, but the value in the extension not.

@yuki I think thats a bug in the chrome extension. I just started noticing this as well. I opened an issue to take a look at this over in that repo: https://github.com/apollographql/apollo-client-devtools/issues/1522.

For now, if you close and reopen devtools, it should update properly. Sorry for the bug!

1 Like