Search code examples
vue.jsgraphqlapolloapollo-clientvue-apollo

How to handle Apollo cache updates for queries with multiple arguments (e.g. tabular data)


Quick summary of my issue

I have a getTracks query that will fetch track data, and takes a handful of arguments for sorting/filtering (limit, offset, search, sort, order). Unfortunately, Apollo caches all of these separately, so when an item is deleted in one view, the user changes the sort order and the deleted item is still there (because it's in the cache for a different set of arguments).

Details

This is the getTracks query along with the relevant input type (in my server-side AppSync schema):

input SelectOptions {
  limit: Int
  offset: Int
  sort: String
  order: String
  search: String
}

type Query {
  getTracks(options: SelectOptions): TrackList!
}

type TrackList {
  total: Int!
  items: [Track!]!
}

In the Vue client, the query looks like this:

query GetTracks($options: SelectOptions) {
  getTracks(options: $options) {
    total
    items {
      id
      name
      runtime
      createdAt
      metadata {
        trackNum
        trackName
        artistName
        albumName
        year
      }
    }
  }
}

The first time the table is shown, the items load up and display fine (default sort order is createdAt/desc). If I sort by createdAt/asc, a network request is made to fetch the results, and they show up fine. Now, if I delete one of the records, and then sort by createdAt/desc again, the deleted item is shown since it's pulling it from the cache.

Is there no way in Apollo to have it intelligently go through and remove (or add/modify) items from the cache even though the query has different arguments? I did try the @connection directive as documented here, but it just made it so sorting the table had no effect (it wouldn't fetch from the server at all).

I'm completely stuck, and ready to just disable caching altogether because it doesn't seem to work in real-world scenarios. How can I keep the default cache-and-network policy, but get my tables to behave properly when data is changed?


Solution

  • Unfortunately, this issue has been brought up before and there's not a good existing solution for it. The biggest problem is that neither the client nor the cache expose any method for fetching all requests for a given query -- if you had that information, you could iterate through the requests and update the cache appropriately.

    The next best thing you can do is utilize apollo-link-watched-mutation. The link lets us effectively identify one or more queries that will need to be updated per mutation, but it uses operation names to identify the queries (and which mutations should trigger them). This way, you don't even need to know the relevant variables to update a query -- you just need to name your operations consistently.

    new WatchedMutationLink(
      cache,
      {
        SaveTodo: {
          TodoList: ({ mutation, query }) => {
            // update logic here
          }
        }
      }
    )
    

    One downside to this approach is that you lose the ability to define your update logic on a per-component basis. However, you can also get around that limitation by using differently named mutations.