TL;DR:
How can I safely update the Apollo client cache after a mutation if the query to update has not run and been cached (ie. right after page reload)?
Problem
The Apollo docs are not very clear on how update (safely or not) a cached query that has never been run (and therefore has no cached data to modify). Trying to use writeQuery
on a query that has no cached data throws an error and obviously doesn't update the query.
- Use
try...catch
during every call towriteQuery
- This really doesn't seem like a valid approach, but might be the only way?
- What about other errors that happen?
- Somehow set a default state for the query cache
- Seems like a lot of work to scaffold an app's initial state, which may prevent queries from running?
Ideas
My only ideas currently involve a try...catch
around the different query update calls, but this seems like extra work. Not only that, but I will potentially be ignoring actual errors that aren't related to a query not existing already.
Example
I have two components, one for a list of the user's personal messages and the other for a list of the user's drafts. Even though all drafts are also a personal message, but I use two different queries for efficiency, as the user will likely always have far fewer drafts than sent messages.
When using the app as normal (ie. loading all queries while navigating around) everything works properly. To create a draft I need to go to Drafts.jsx
(loads the Drafts query, important!) and click a button to create a draft. Upon creating the draft, I run the update
function seen below (need to update both queries since their internal arguments are different). The cache update for the "Drafts" query works as expected, because the query has already been executed and data stored in the cache. However, since I have never visited the MyMessages page, the "MyMessages" query fails and throws an error.
Code
My messages query (in MyMessages.jsx
)
const RECEIVED_MESSAGES_QUERY = gql`
query GetGroup($groupSlug: String!) {
groupEdge(groupSlug: $groupSlug) {
node {
id
name
slug
allMessages(author: true) {
edges {
node {
id
status
subject
preview
}
}
}
}
}
}
`;
Drafts query (in Drafts.jsx
):
const DRAFT_MESSAGES_QUERY = gql`
query GetGroup($groupSlug: String!) {
groupEdge(groupSlug: $groupSlug) {
node {
id
name
slug
allMessages(author: true, includeStatus: ["draft"]) {
edges {
node {
id
status
subject
preview
}
}
}
}
}
}
`;
Mutation (in CreateDraft.jsx
):
// Mutation file
const draftsData = proxy.readQuery({
query: MemberDraftsQuery,
variables: { groupSlug },
});
console.log('draftsData', draftsData);
// NOTE: Fails here (doesn't progress to next console statement)
const messagesData = proxy.readQuery({
query: MemberMessagesQuery,
variables: { groupSlug },
});
console.log('messagesData', messagesData);
// Add the new draft edge to the member's drafts
draftsData.groupEdge.node.allMessages.edges.push(createdDraftEdge);
// Add the new draft edge to the member's authored messages (uses different query)
messagesData.groupEdge.node.allMessages.edges.push(createdDraftEdge);
proxy.writeQuery({
query: MemberDraftsQuery,
data: draftsData,
variables: { groupSlug },
});
proxy.writeQuery({
query: MemberMessagesQuery,
data: messagesData,
variables: { groupSlug },
});
Related?
StackOverflow - How to update apollo cache after mutation query with filter
This article touches on the same aspect as me, but I am already handling the variables
in writeQuery
. However, does this mean that I should try...catch
with every writeQuery
operation? What if it fails for some other reason? All in all, not sure that it answers my question.
EDIT 1
After some more coding (and seen in comment below), I realized that readQuery
was causing the issue (not writeQuery
). Since I didn't like using try...catch
because it would mix errors and not know what caused the error, I have updated my approach to make the try...catch
only handle readQuery
errors. If an error is thrown here, I assume that either the query didn't exist (should handle this separately) or that the cached data didn't exist. In both cases, I simply skip writing to that query.
import { MemberDraftsQuery } from '@componens/MemberDrafts';
...
class MemberDraftDetails extends Component {
onSubmit = (groupSlug) => {
mutate({
variables: {
input: {
content,
subject,
...
}
},
update: (proxy, { data: responseData }) => {
const draftEdge = responseData.createDraft;
// UNUSED: Could be used to validate the query actually exists (doesn't warn for some reason)
const hasDraftsQuery = Boolean(MemberDraftsQuery);
let hasDraftsQueryCache = true;
// A failure to read the query means that there is no cached data (therefore cannot read/write)
// NOTE: Could write default data, but no reason to (since it will be fetched later)
try {
const draftsData = proxy.readQuery({
query: MemberDraftsQuery,
variables: { groupSlug },
});
} catch (e) {
hasDraftsQueryCache = false;
}
// Only add the draft to the cache if it exists. If not, it will be queried and retrieved later
if (hasDraftsQueryCache) {
const newDraftEdges = data.groupEdge.node.allMessages.edges.concat(draftEdge);
data.groupEdge.node.allMessages.edges = newDraftEdges;
proxy.writeQuery({
query: MemberDraftsQuery,
data,
variables: { groupSlug },
});
}
}
}).then().catch();
}
}