I’m trying to understand how type policies could possibily replace all instances of updateQuery
or cache writes in general. Current documentation and deprecation warnings are all pushing us to migrate our fetchMore
updateQuery
cache writes to type policies merge functions, but from what I can tell for even fairly mundane use cases type policies cannot possibly have enough context in order to know how to update the cache.
I’m hoping someone can explain how I am mistaken.
Two objections I have to the current deprecation warnings, 1) a ease of use concern about type policies, 2) a substantial objection:
1) Type policies are significantly more complicated to configure and more error prone than cache writes using writeQuery
:
- They demand an understanding of cache behaviour around normalisation and querying the cache using sucessive
readField
function calls. This creates a very high barrier to entry for my team to be able to do anything with paginated queries.writeQuery
is often just a setter where you are dealing with more understandable operation types, and/or you just concating or filtering an array of your operation types.
- In a TypeScript context you are pretty much flying blind even when using codegen tooling such as
TypedTypePolicies
fromgraphql-codgen
because nearly everything within amerge
orread
method isany
.- in contrast
writeQuery
andupdateQuery
have excellent static analysis support provided you useTypedDocumentNode
produced fromgraphql-codgen
- in contrast
2) A type policy lacks sufficient context to be able to replace updateQuery
or writeQuery
With the current interface its impossible to know what the appropriate merge implementation should be, and as far as I can tell it can never replace writeQuery
or updateQuery
.
Consider the following type policy below. I have a Ticket
type that has a field called messages
that takes mulitple args and returns a cursor based paged query result with non-normalisable edges
that contain a message type that do have an id that can be normalised by the default policies.
const ticketPolicy: TypedTypePolicies = {
Ticket: {
fields: {
messages: {
keyArgs: ["order"],
},
},
},
PagedTicketMessages: {
keyFields: false,
merge: false,
fields: {
edges: {
merge(existing = [], incoming, util) {
// impl here
},
},
},
},
PagedTicketMessagesEdge: {
keyFields: false,
merge: false,
},
};
Now if I use fetchMore
to get the next page I basically want to concat the incoming result with the existing result, which would suggest this as my type policy:
merge(existing = [], incoming, util) {
// or the concat utility that comes with apollo
return [...existing, ...incoming];
}
However, I also have mutation operations that delete, re-order, and update the items in this list. Assume I have this delete mutation:
mutation deleteTicketMessage($messageId: ID!) {
deleteTicketMessage(messageId: $messageId) // return Boolean
}
and I have this update
mutatation cache write:
update: (cache, _, { variables }) => {
const messages = cache.readQuery({
query: GetTicketMessagesDocument,
variables: { ticketId },
});
if (messages) {
const edges = messages.ticket.messages.edges.filter(
(edge) => edge.message.id !== variables.messageId
);
cache.writeQuery({
query: GetTicketMessagesDocument,
variables: { ticketId },
data: {
...messages,
ticket: {
...messages.ticket,
messages: { ...messages.ticket.messages, edges: edges },
},
},
});
}
}
So now my merge function will be invoked by two different kinds of operations - a fetchMore
that gets the next page where incoming
will be a set of results meant for concatenation, and a delete operation where incoming
will be the same as existing
but with the deleted message filtered out.
From the point of view of the merge function I have essentially zero context about which one of these operations is being executed, and I have no hint from any of the arugments as to whether I should accept the incoming or concat with the existing. All I have is the arguments for the original query, but nothing else.
The same dilemma repeats itself for any other operation I might want to have on this list. In this context I have no choice but to completely ignore the deprecation warning and use updateQuery
instead of a type policy. There is no reasonable way for me to implement this type policy.
Am I completely wrong about this, or has there been a complete oversight about how inadaquate the current type policy interface is for replacing updateQuery
?