question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Apollo cache: reuse same derived data between different fields

See original GitHub issue

I have an server schema like this:

User {
 id
 amount
}

Match {
 id
 date
 probability
}

Then I need to compute derived data before render. Then I want to do something like this:

const typePolicies = {
 Match {
  fields: {
   odd: {
    read (_ , { readField })  {
      
      const amountUser = cache.readFragment({
        id: 'User:{}',
        fragment: gql`
          fragment amountUser on User {
           amount                          
          }`
      });

      const probabilityMatch = readField('probability');    
      
      const calc = getCalc (amountUser, probabilityMatch); // complex operation
      const odd = getOdd (calc);
      return odd;
    }
   } ​,
   maxValue: {
    read (_ , { readField })  {
      
      const amountUser = cache.readFragment({
        id: 'User:{}',
        fragment: gql`
          fragment amountUser on User {
           amount                          
          }`
      });

      const probabilityMatch = readField('probability');    
      
      const calc = getCalc (amountUser, probabilityMatch); // complex operation
      const maxValue = getMaxValue (calc);
      return maxValue;
    }
   } ​
  ​}
 ​}
}

Then query like this:

query Match {
   Match {
     id
     date
     odd @client
     maxValue @client
  }
}

But I’m executing same complex operation twice. An idea would be return nested fields:

const typePolicies = {
 Match {
  fields: {
   computed: {
    read (_ , { readField })  {
      
      const amountUser = cache.readFragment({
        id: 'User:{}',
        fragment: gql`
          fragment amountUser on User {
           amount                          
          }`
      });

      const probabilityMatch = readField('probability');    
      
      const calc = getCalc (amountUser, probabilityMatch); // complex operation
      const odd = getOdd (calc);
      const maxValue = getMaxValue (calc);
      return {
         odd,
         maxValue
      }
     }
    } ​
   } ​
  ​}
​ }
}

Then query like this:

query Match {
   Match {
     id
     date
     computed @client {
      odd
      maxValue
     }
  }
}

But I’m creating an type computed that I don’t want. Is there an optimal way to solve this?

Issue Analytics

  • State:open
  • Created 2 years ago
  • Comments:7 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
benjamncommented, Apr 26, 2021

@jdmoliner Two notes that I hope will get you unstuck, though I imagine you’ll have further questions:

  1. You can reuse readField instead of cache.readFragment:
read(_, { readField, cache, toReference }) {
  const userRef = toReference(cache.identify({ __typename: "User", ... }));
  const amountUser = readField("amount", userRef);
  ...
}
  1. There’s a handy object called storage in the read function options that you can use for caching:
read(_, { readField, cache, toReference, storage }) {
  // Returned cached storage.odd value, if defined:
  if (storage.odd !== void 0) return storage.odd;
  const userRef = toReference(cache.identify({ __typename: "User", ... }));
  const amountUser = readField("amount", userRef);
  ...
  // Cache the final result:
  return storage.odd = odd;
}

This storage object is private to this field within this specific object, but the same storage object is provided to read and merge and cache.modify functions, so (if you later want to invalidate the storage.odd cache) you can either define a merge function that allows deleting storage.odd (perhaps a bit awkward for a client-only field), or call cache.modify like so:

cache.modify({
  id: cache.identify({ __typename: "Match", ... }),
  fields: {
    odd(value, { storage }) {
      // Clear the cached storage.odd value:
      delete storage.odd;
      // Preserve any existing value:
      return value;
    },
  },
})

Please let me know if I can clarify anything about these somewhat obscure corners of the type/field policies API.

0reactions
benjamncommented, May 18, 2021

@josedavid2021 That’s very clever, and seems like it should work! However, I hope we can come up with something (along the lines of #8078) so you won’t need that finalize @client trick.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Customizing the behavior of cached fields - Apollo GraphQL
You can customize how a particular field in your Apollo Client cache is read and written. To do so, you define a field...
Read more >
Advanced topics on caching in Apollo Client
This article describes special cases and considerations when using the Apollo Client cache. Bypassing the cache. Sometimes you shouldn't use the cache for...
Read more >
Interacting with cached data - Apollo GraphQL Docs
If your cache contains all of the data necessary to fulfill a specified query, readQuery returns a data object in the shape of...
Read more >
Reading and writing data to the cache - Apollo GraphQL Docs
You can read and write cache data using GraphQL queries that are similar (or even identical) ... If your cache contains data for...
Read more >
Customizing the behavior of cached fields - Client (React)
You can customize how a particular field in your Apollo Client cache is read and written. To do so, you define a field...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found