Search code examples
apollo-clientreact-apollo

Is it possible to update or replace a cached entry's id with a new id in Apollo Client?


So I have a station in my app. Here's an example object:

interface Station {
  id: number; // auto-increment
  stationId: number; // this is a constant
  name: string;
  status: 'live' | 'draft';
}

When updating the station the stationId will never change, but sql will auto increment the id field. The station's status will also be updated to 'draft'. e.g.

const previousStation: Station = {
  id: 1,
  stationId: 123456,
  name: 'Example splet right',
  status: 'live',
}

// ... then some sql-fu

const nextStation: Station = {
  id: 2,
  stationId: 123456,
  name: 'Example spelt right',
  status: 'draft',
}

Apollo client is unable to infer that nextStation should replace previousStation, so there are a lot of mutations that require readQuery/ readFragment.

I was hoping there may be a simpler solution, but the complexity arises from the id changing. If I could find all references to its object-id in the normalised data cache and update those fields. Is something like this even possible?


Solution

  • Solved. The key was to use a read/write fragment, using the old object-id, but passing in the new id as a reference. Then evict the old reference from the cache. It looked similar to this:

    mutation({
        variables,
    
        update(cache, { data }) {
            if (!data) {
                return;
            }
    
            const { id: newId, __typename } = data.station;
    
            const existing = cache.readQuery({
                query: GET_STATION_QUERY,
                variables: { id: variables.id }
            });
    
            if (existing) {
                const newStation = {
                    ...(existing.getStation || {}),
                    ...(data.updateStation || {})
                };
    
                cache.writeQuery({
                    query: GET_STATION_QUERY,
                    variables: { id: newId },
                    data: { station: newStation }
                });
            }
    
            const oldObjectId = `${__typename}:${variables.id}`;
    
            const fragment = gql`
                fragment MyFragment on SomeType {
                    station {
                        id
                    }
                }
            `;
    
            const existingStationOnMyType = cache.readFragment({
                id: oldObjectId,
                fragment
            });
    
            if (existingStationOnMyType) {
                cache.writeFragment({
                    id: oldObjectId,
                    fragment,
                    data: {
                        id: newId
                    }
                });
    
                cache.evict({ id: oldObjectId });
                cache.gc();
            }
        }
    });