In react-apollo 2.0.1 I have a graphql type that looks like this:
type PagedThing {
data: [Thing]
total: Int
}
When doing the following writeFragment
client.writeFragment({
id,
fragment: gql`
fragment my_thing on Thing {
status
}
`,
data: {
status
}
})
The cache is not update and the new data is not shown on the UI. Is there something else that need to be done?
PS: To be safe I used fragment matching
Edit 1:
I receive an error of:
Cannot match fragment because __typename property is missing: {"status":"online"}
So I changed the code to:
client.writeFragment({
id,
fragment: gql`
fragment my_thing on Thing {
status
}
`,
data: {
__typename: 'Thing',
status
}
})
And no error is thrown but the updated still do not happen
Turns out I needed to not only add the __typename as the ID needed to be the one resolved by default (Explained here)
So I needed to do the following in order to make it work:
client.writeFragment({
id: `Thing:${id}`,
fragment: gql`
fragment my_thing on Thing {
status
}
`,
data: {
__typename: 'Thing',
status
}
})
it appears that the framework has been extended to provide a method called 'identify' to avoid this problem (in case they change implementation later)
cache.modify({
id: cache.identify(myPost),
^
fields(fieldValue, details) {
return details.INVALIDATE;
},
});
https://www.apollographql.com/docs/react/caching/cache-interaction/#example-invalidating-fields-within-a-cached-object
Related
Suppose I have the following GraphQL types:
type User {
id: String!
posts: [Post!]!
}
type Post {
id: String!
text: String,
}
And here is a mutation that returns the updated post:
mutation addNewPost(
$userId: String!
$text: String!
) {
addNewPost(userId: $userId, text: $text) {
id
text
}
}
After running this mutation my cache contains a new entry of a post. How do I add it to the user's posts array? I have tried cache.writeQuery and cache.modify but I cannot figure it out.
We do push the item into array inside the update function, which is one of the options of useMutation.
I'm writing the whole mutation so that you can get the idea 💡, let have a look at example:
By Update function:
const [createPost, { data, loading, error }] = useMutation(CREATE_POST, {
update(cache, response) {
// Here we write the update
// Step 1: Read/Fetch the data 👈
const data = client.readQuery({
query: FETCH_POSTS_QUERY,
});
// Step 2: Update the cache by immutable way 👈
client.writeQuery({
query: FETCH_POSTS_QUERY,
data: {
getPosts: [response.data.createPost, ...data.getPosts],
},
});
},
variables: formValues,
});
By refetchQueries:
That's really shortcut 🔥 way to update the cache, by using DocumentNode object parsed with the gql function
const [createPost, { data, loading, error }] = useMutation(CREATE_POST, {
refetchQueries: [ 👈
FETCH_POSTS_QUERY
'fetchPosts`' // OR Query name
],
variables: formValues,
});
You're going to want to directly write to the Apollo cache in order to update the other entities that your mutation has modified.
Have a look at the docs https://www.apollographql.com/docs/react/data/mutations/#making-all-other-cache-updates here for specifics (you're going to want to use cache.modify and cache.writeFragment)
I'm getting the following error despite the mutation being wrapped in the gql tag:
Invariant Violation: Expecting a parsed GraphQL document. Perhaps you need to wrap the query string in a "gql" tag? http://docs.apollostack.com/apollo-client/core.html#gql
This issue is only caused by the mutation code below, I have a query that works.
Code:
<script>
import gql from 'graphql-tag'
export default {
apollo: {
createTask: {
mutation: gql`
mutation Task(
$taskName: String!
$taskDesc: String!
) {
setSession(
taskName: $taskName
taskDesc: $taskDesc
) {
id
taskName
}
}
`,
variables() {
return {
taskName: this.res.data.task_name,
taskDesc: this.res.data.task_description,
}
},
},
},
data() {
return {
createTask: '',
}
},
}
<script>
Smart queries are declared using the apollo object and are ran when the component mounts. If you have queries that you'd like to run this way, then your code would look something like this:
export default {
apollo: {
someQuery: gql`...`,
}
}
However, when working with mutations, you generally don't want to run them on mount. Instead, you want to execute the mutation in response to some user action, like a button click. So the above syntax won't work for mutations. Instead, you need to call this.$apollo.mutate directly inside one of your methods:
export default {
methods: {
createTask() {
this.$apollo.mutate({
mutation: gql`...`,
variables: {...},
// other options
}).then(result => {
// do something with the result
})
}
}
}
I had this problem recently and it was because of an issue with an async import
I am using vue-apollo
async account () {
return {
query: (await import('src/modules/accounts/graphql/accounts.list.query.gql')),
......
I just had to replace that import with a require and it was happy again.
async account () {
return {
query: require('src/modules/accounts/graphql/accounts.list.query.gql'),
......
Using this mutation:
import produce from 'immer
const [createItem] = useMutation(CREATE_ITEM, {
update (client, { data: { createItem } }) {
const queryResults = client.readQuery({
query: GET_LATEST_ORDER,
variables: { orderDepth: 1 }
})
client.writeQuery({
query: GET_LATEST_ORDER,
variables: { orderDepth: 1 },
data: produce(queryResults, draft => {
draft.orders[0].items.push(createItem)
})
})
}
})
I am unable to get
const { loading, data, refetch } = useQuery(GET_LATEST_ORDER, {
variables: { orderDepth: 1 }
})
to show updated data after the mutation.
The apollo cache is updated correctly. But data on the useQuery does not change.
The issue ended up being the returned object from the mutation was not exactly the same. It was missing an #client field.
While obvious in hindsight no where I searched described this as a reason except for a comment I saw mentioning perhaps missing the __typename.
This would have been obvious had apollo thrown an error. However, no error was thrown, nor existed on the useQuery.
I am trying to use Apollo cache for local state management to store the state of a form so it can be returned to without clearing.
I am experiencing a problem where the cache is being updated but subsequent queries to the cache are returning stale data. I have experienced this problem in React components using the useQuery hook, and also in Apollo DevTools which I will use to demonstrate it below:
I have this mutation and query set in my resolvers (I am using Typescript):
const resolvers = {
Mutation: {
storeLetterDraft: (_root, args: { type: string, details: LetterSending }, { client, getCacheKey }) => {
const id = getCacheKey({
__typename: "LetterDraft",
id: args.type,
});
const data = { ...args.details };
client.writeFragment({
data,
id,
fragment: LETTER_SENDING_FRAGMENT,
});
},
},
Query: {
letterDraft: (_root, args: { type: string }, { client, getCacheKey }) => {
// I HAVE TRIED A DEBUGGER STATEMENT HERE
const id = getCacheKey({
__typename: "LetterDraft",
id: args.type,
});
return client.readFragment({
id,
fragment: LETTER_SENDING_FRAGMENT,
});
},
},
}
My fragment is:
export const LETTER_SENDING_FRAGMENT = gql`
fragment DraftLetterSending on LetterDraft {
date
firstName
lastName
addressLine1
addressLine2
addressTown
addressCounty
addressPostcode
}
`;
I am initialising my cache with:
cache.writeData({
data: {
letterDrafts: [{
__typename: "LetterDraft",
id: "CREATE",
addressCounty: "Northamptonshire",
addressLine1: "1 Watkin Terrace",
addressLine2: "",
addressPostcode: "NN1 3ER",
addressTown: "Northampton",
date: "2019-11-01",
firstName: "d",
lastName: "d",
}],
},
});
My mutation looks like:
export const storeCreateLetterSendingMutation = gql`
mutation StoreCreateLetterSending($details: LetterSending!) {
storeLetterDraft(type: "CREATE", details: $details) #client
}
`;
Before mutation, the cache in Apollo DevTools looks as expected:
And a query returns as expected:
After the mutation is performed, the cache updates:
However, running the query again results in the stale data:
Interestingly if I put a debugger statement in the part above (I HAVE TRIED A DEBUGGER STATEMENT HERE), then it seems the query resolver is run the first time, but not the second time, so it appears the query is being cached - even though it is the cache I am updating! Therefore I think the issue is with the query not running the resolver subsequently.
I had missed this from the documentation (there are various places on the Apollo website detailing the local cache and #client.
https://www.apollographql.com/docs/react/essentials/local-state/#forcing-resolvers-with-clientalways-true
While leveraging the cache for both local and remote results can be super helpful in a lot of cases, it's not always the best fit. We might want to use a local resolver to calculate a dynamic value that needs to be refreshed on every request, while at the same time continue to use the cache for the network based parts of our query. To support this use case, Apollo Client's #client directive accepts an always argument, that when set to true will ensure that the associated local resolver is run on every request.
Im using React with Apollo (Apollo Client v2). I have group query which needs to return a single group.
This code is working but I've hard coded HARD-CODED-ID. How can I instead pass the ID as a string from the React component?
In my React component:
const groupQuery = gql`
query Group {
group {
_id
name
}
}
`;
export default graphql(groupQuery, {
props: ({ data }) => ({ ...data }),
})(GroupPage);
My resolver:
Query: {
groups() {
return Groups.find().fetch();
},
group() {
return Groups.findOne('HARD-CODED-ID');
},
}
There's three things that you'll need to do:
1.) If you haven't already, modify the schema on your server so that your query accepts the id as an input, for example:
type Query {
#other queries
group(id: ID!): Group
}
2.) Modify your resolver so that it handles the passed-in id. Assuming you're using graphql-tools:
group(root, { id }) {
return Groups.findOne(id); // did you mean something like findOne({id}) ?
},
3.) Modify your client-side code. Typically, you'll make the id a prop you pass in to your component, and then use that as a variable in your request.
const groupQuery = gql`
query Group($id: ID!) {
group(id: $id) {
_id
name
}
}
`;
// assuming that the component prop is called groupId
export default graphql(groupQuery, {
options: ({ groupId }) => ({
variables: { id: groupId },
}),
})(GroupPage);
Instead of an object, options can be a function, in which case it's passed the component's props as its first parameter. You can then use those props to define the variables your query will use. You can read more about using variables with Apollo client here and here.