GraphQL: Handling Entities with and without an 'ID' Field

  • prerequisites: Ensure that you have installed the Apollo extension for the Chrome browser. This extension will allow you to check for data changes in the cache.
  • Case 1: Handle entities without 'id' field
    
    query
    const GET_BOOKS = gql`
    query {
      books {
      name
      genre
      }
    }
    `;
    Entity name: 'books'
    An object has multiple keys, for instance, {__typename:'Book", name:'Harry Portal", genre:"Fiction"} ,
    
    update an object in local cache with keys instead of 'id' field
    
    // Update a record without 'id' field in local cache
    // for instance, the entity is "books", and the record is below:
    // __typename: "Book"
    // name: "grapes 🍇 delicious"
    // genre: "Fiction"
    // updateCachedRecordWithoutId('books',{name:'oldname'},{name:'newname',age:12})
    updateCachedRecordWithoutId>(
    cacheEntity: string,
    filter: T,
    data: T
    ): void {
    this.apollo.client.cache.modify({
    id: 'ROOT_QUERY',
    fields: {
    [cacheEntity]: (items = []) => {
      const oldItems = cloneDeep(items);
      const index = oldItems.findIndex((rec: T) =>
      Object.keys(filter).every((key) => {
        const k = key as keyof T;
        return rec[k] === filter[k];
      })
      );
      if (index < 0) return items;
      const newItems = cloneDeep(items);
      newItems[index] = { ...newItems[index], ...data };
      return newItems;
      },
    },
    });
    }
    Remove an object from an entity with keys instead of 'id' field
    
    /**
    * Remove a record without 'id' field from an entity in apollo cache
    * This method is used when you need to remove the cache with a custom key.
    *
    * @param cacheEntity The name of the entity in the cache.
    * @param filter The key or keys of the entity you want to update.
    * @example
    * removeCachedRecordWithoudId('workorderliness', { workOrderNo:'abcd',lineNo:10000});
    * */
    removeCachedRecordWithoudId(
      cacheEntity: string,
      filter: Partial extends object ? Partial : never
    ): void {
    this.apollo.client.cache.modify({
      id: 'ROOT_QUERY',
      fields: {
        [cacheEntity]: (items = [], { DELETE }) => {
        const oldItems = cloneDeep(items);
        const newItems = oldItems.filter(
        (rec: T) =>
          !Object.keys(filter).every((key) => {
          const k = key as keyof T;
          return rec[k] === filter[k];
        })
      );
      // If the record is empty, delete it.
      // This is to prevent the cache from returning an empty array. Which causes an error when adding a f
      if (newItems.length === 0) return DELETE;
      return newItems;
      },
      },
      });
    }
    

  • Case 2:Handle entities with 'id' field
    query
    const GET_BOOKS = gql`
    query {
      books {
        id
        name
        genre
        }
      }
    `;
    Entity name: 'books'
    In Apollo cache, An object has only one key, for instance, {__ref:'Book:640555"} ,
    
    /**
    * Update a single record using id in apollo cache.
    *
    * @param objectName The name of the object in the cache, which is capitalized, such as 'Book'. However, the
    * @param id The id of the object, which is unique for the entity, refering to the record in local cache, s
    * @param data The data to update the entity with.
    * @example
    * updateCachedRecordById('Book', '1233', {age: 12, name: 'New Name' });
    *
    */
    updateCachedRecordById>(
      objectName: string,
      id: string,
      data: T
    ): void {
      const fields = Object.keys(data)
      .map((field) => `${field}`)
      .join('\n');
      // MyObject is a random fragment name, which can be any name,but should be constant instead of a variable
      const fragmentQuery = gql`
      fragment MyObject on ${objectName} {
        ${fields}
      }
      `;
      this.apollo.client.writeFragment({
      id: objectName + ':' + id,
      fragment: fragmentQuery,
      data: data,
      });
    }
    /**
    * Remove a record using id from an entity in Apollo cache .
    * This method is limited to entities with an 'id' field.
    *
    * @param cacheEntity The name of the entity in the cache.
    * @param id The id of the entity.
    * @example
    * removeCachedRecordByid('equipments', '1233');
    *
    */
    removeCachedRecordByid(cacheEntity: string, id: string): void {
      this.apollo.client.cache.modify({
      id: 'ROOT_QUERY',
    fields: {
      [cacheEntity]: (items: { __ref: string }[] = [], { readField }) => {
      const i = items.findIndex(
        (rec: { __ref: string }) => readField('id', rec) === id
      );
      if (i < 0) return items;
        const newRecords = cloneDeep(items);
        newRecords.splice(i, 1);
        return newRecords;
      },
      },
      });
    }