tuff
tuff

Reputation: 5153

Update Apollo cache after object creation

What are all the different ways of updating the Apollo InMemoryCache after a mutation? From the docs, I can see:

  1. Id-based updates which Apollo performs automatically
    • Happens for single updates to existing objects only.
    • Requires an id field which uniquely identifies each object, or the cache must be configured with a dataIdFromObject function which provides a unique identifier.
  2. "Manual" cache updates via update functions
    • Required for object creation, deletion, or updates of multiple objects.
    • Involves calling cache.writeQuery with details including which query should be affected and how the cache should be changed.
  3. Passing the refetchQueries option to the useMutation hook
    • The calling code says which queries should be re-fetched from the API, Apollo does the fetching, and the results replace whatever is in the cache for the given queries.

Are there other ways that I've missed, or have I misunderstood anything about the above methods?

I am confused because I've been reading the code of a project which uses Apollo for all kinds of mutations, including creations and deletions, but I don't see any calls to cache.writeQuery, nor any usage of refetchQueries. How does the cache get updated after creations and deletions without either of those?

In my own limited experience with Apollo, the cache is not automatically updated after an object creation or deletion, not even if I define dataIdFromObject. I have to update the cache myself by writing update functions.

So I'm wondering if there is some secret config I've missed to make Apollo handle it for me.

Upvotes: 1

Views: 1600

Answers (1)

Daniel Rearden
Daniel Rearden

Reputation: 84657

The only way to create or delete a node and have Apollo automatically update the cache to reflect the change is to return the parent field of whatever field contains the updated List field. For example, let's say we have a schema like this:

type Query {
  me: User
}

type User {
  id: ID!
  posts: [Post!]!
}

type Post {
  id: ID!
  body: String!
}

By convention, if we had a mutation to add a new post, the mutation field would return the created post.

type Mutation {
  writePost(body: String!): Post!
}

However, we could have it return the logged in User instead (the same thing the me field returns):

type Mutation {
  writePost(body: String!): User!
}

by doing so, we enable the client to make a query like:

mutation WritePost($body: String!){
  writePost(body: $body) {
    id
    posts {
      id
      body
    }
  }
}

Here Apollo will not only create or update the cache for all the returned posts, but it will also update the returned User object, including the list of posts.

So why is this not commonly done? Why does Apollo's documentation suggest using writeQuery when adding or deleting nodes?

The above will work fine when your schema is simple and you're working with a relatively small amount of data. However, returning the entire parent node, including all its relations, can be noticeably slower and more resource-intensive once you're dealing with more data. Additionally, in many apps a single mutation could impact multiple queries inside the cache. The same node could be returned by any number of fields in the schema, and even the same field could be part of a number of different queries that utilize different filters, sort parameters, etc.

These factors make it unlikely that you'll want to implement this pattern in production but there certainly are use cases where it may be a valid option.

Upvotes: 4

Related Questions