houdini icon indicating copy to clipboard operation
houdini copied to clipboard

Imperative cache API

Open AlecAivazis opened this issue 4 years ago • 2 comments

Houdini needs to provide the user a way to interact with the cache imperatively to handle situations that aren't covered by the declarative API

AlecAivazis avatar Mar 05 '21 19:03 AlecAivazis

The biggest hurdle atm is how to compute the storage key for particular piece of data in the cache. I think the current selection object is not friendly enough for a user facing API so I think we need to come up with something different.

Fundamentally, writing data to the cache relies on 3 things besides the value: the ID of the object, the key of the field, and the type of the value (to marshal any complex values the user might provide).

Some use cases the API should cover:

  • Providing a new value for a field or link
  • Marking a certain field as stale
  • Refresh all current queries/stores

Some random thoughts:

  • if schema information is stored as users navigate the page it must be tied into garbage collection so we can delete it
    • this complicates manually preloading data since the schema isn't known before hand.
  • must be written to the top-most non-optimistic layer of the cache at write time
  • specifying the key manually can be made more ergonomic if we hide it behind something like myObject.getField("firstName", {arg: "Hello"})
  • store api's can be used to embed type information without having to store it in some global place
    • ie MyFragmentStore.writeToCache({ }) or MyFragmentStore.preload but that might be confusing on a QueryStore
    • Maybe something like cache.writeDocument({ document: MyFragmentStore, data: {}, variables: {} }) is better since its more clear its happening to the store itself
    • Specify a fragment means asking for the parent ID means giving the user a way to calculate the ID that doesn't couple them to the exact format used internally
  • are these methods on the cache, or methods on objects that represent an entity in the cache?
    • cache.write("User", "2", { ... }) looks alright but writeLink gets really gnarly since you need to embed a parent and a target which is just asking for arbitrary names that are hard to remember.

AlecAivazis avatar Mar 21 '22 08:03 AlecAivazis

Not an easy topic, thx for putting a starting point 👍 I would also like to collect "Use Cases" to understand the different things to handle and provide THE good API. I'll put notes here when I hit some

jycouet avatar Jul 17 '22 22:07 jycouet

@AlecAivazis @jycouet so I found an interesting use case for this (I'm actually doing this on one of my projects):

  • so I have an entity (let's call it A) that has a list of other entity (let's call it B)
  • I have a mutation to delete B, and when deleting B I need to update A to reflect those changes on the screen
  • so I call the B delete mutation, and then I get all B from the list inside A to filter the deleted B
  • then I call Cache.write() function to update A with the new list of B

m4tr1k avatar Oct 26 '22 23:10 m4tr1k

After a call with @AlecAivazis , let's put down a few thoughts:

// reset everything 
cache.markAllStale()

// root
const rootCache = cache.getRoot()

// reset all `quoteEdge`
rootCache.markStale("quoteEdge")
// GQL_quoteEdge.markStale();

// reset some fields
const quoteEdge = rootCache.getFields("quoteEdge")
quoteEdge.markStale("nodes")
quoteEdge.markStale("totalCount")
quoteEdge.set("totalCount", 10)

// set a reference
user.set("favoriteFood", cache.get("Recipe", {id: 5})

// reset some fields when specific inputs
rootCache.markStale("quoteEdge",  {edge: { pagination: { skip: 0 }, filters: { creatorUserId: null} }})
rootCache.markStale("user", { name: 'JYC'} )

// add an updater to the layer 
quoteEdge.set("totalCount", count => count + 1)

A lot of things are subject to change... but it's a start 😊

jycouet avatar Nov 10 '22 08:11 jycouet

Don't have this is a HUGE BLOCKER for us to switching from urql to houdini.

I wish you to work on it expressing the best of yourself. 😄

frederikhors avatar Nov 10 '22 10:11 frederikhors

@frederikhors it's looking like this will be the next thing we focus on. stay tuned!

AlecAivazis avatar Nov 10 '22 10:11 AlecAivazis

@frederikhors mind telling me which features/ use cases you need most out of the imperative api? I'm assuming marking data as stale is an important one but I'm curious if there are others?

AlecAivazis avatar Dec 08 '22 20:12 AlecAivazis

For now I only need invalidation.

We're using it hard because we invalidate & refetch in case of update/delete of an item.

I'll let you know if something else is needed.

frederikhors avatar Dec 08 '22 20:12 frederikhors

Awesome! Thanks for the quick reply. Would you want a way to automatically trigger refetches of the deleted value or is that something you would do by hand? Or just the next time the query is fired?

AlecAivazis avatar Dec 08 '22 20:12 AlecAivazis

I think this depends each time.

All the options? It's difficult?

frederikhors avatar Dec 08 '22 21:12 frederikhors

Nope, not difficult just trying to figure out the priority for all of the features. There's a lot that I'd like to be able to do with this API but with 1.0 coming soon I'll have to shift focus over to accommodating some of the recent changes that will effect houdini so I'm trying to get something out quickly before I get distracted

AlecAivazis avatar Dec 08 '22 21:12 AlecAivazis