Updating Apollo Cache for external query after entity mutation - react-apollo

I'd like to display a list of users, based on a filtered Apollo query
// pseudo query
if (user.name === 'John) return true
User names can be edited. Unfortunately, if I change a user name to James, the user is still displayed in my list (the query is set to fetch from cache first)
I tried to update this by using cache.modify:
cache.modify({
id: cache.identify({
__typename: 'User',
id: userId,
}),
fields: {
name: () => {
return newName; //newName is the input new value
},
},
});
But I'm not quite sure this is the correct way to do so.
Of course, if I use refetchQueries: ['myUsers'], I get the correct result, but obviously, this is a bit overkill to refetch the whole list every time a name is updated.
Did I miss something?

Related

Multiple queries in prisma graphql resolver

Following this example here:
https://github.com/prisma/prisma-examples/blob/latest/javascript/graphql-sdl-first/src/schema.js
Let's say I have a mutation where I want to update multiple users by passing their ids and emails accordingly. I know the updateMany would probably be the most suitable option, but since different users would have different values, not sure how to pass that without calling multiple resolvers separately. Something like this:
updateUsers: (_parent, args, context) => {
return context.prisma.user.updateMany({
where: {
id: { in: args.userIds },
},
data: {
email: ??? <--- use args.emails here
}
})
}
or should I just run multiple mutations:
updateUsers: async (_parent, args, context) => {
try {
args.emails.forEach(email => {
const user = await context.prisma.user.update({
where: {
id: { in: args.userIds },
},
data: {
email: args.email
}
})
return user;
}
} catch (error) {
console.log(error)
}
}
not sure if the last example would even work because of multiple return statements, since all the resolvers have a return statement, how can I run multiple queries/mutations?
updateMany allows you to bulk update all rows matching certain conditions with the same data. It does not help you in this case.
You could loop over an array of users to update id and email of each of them. Your approach will not work however, as you seem to have two arrays (a list of user IDs and a list of email addresses). However, instead of fixing your code I'd suggest to change the GraphQL interface you've defined.
Option 1) is close to what you did. You keep a bulk mutation updateUsers, but instead of two lists (a list of user IDs and a list of email addresses), it should accept a list of users, each of them having an user ID and an email address.
To improve performance you could use Promise.all and not wait for each update to happen, before starting the next one.
Option 2) is the preferable. Instead of a bulk mutation updateUsers, I would create a mutation updateUser that updates only one user. If client wants to update multiple users in the same request, they can! A single GraphQL request can contain multiple mutations.

Why I am I getting the same value for a field in all of my array objects for useQuery?

I have a useQuery that returns data of the first item inserted. For context, I have an ecommerce website with grocery products, suppose I add an apple with quantiy of 4. Next, when I add another order with quantiy 10, it adds correctly in the database and I get correct results in the apollo playground. But when I am pulling data using the below code in Apollo client it has all the orders of that user with different order ids but has the quantiy of the first order made for apple.
const { loading, error, data } = useQuery(queries.GET_USER_ORDERS, {
fetchPolicy: "cache-and-network",
variables: {
userId: currentUser.uid,
},
});
Graphql query:
const GET_USER_ORDERS = gql`
query Query($userId: String) {
userOrders(userId: $userId) {
_id
userId
products {
_id
name
price
orderedQuantity
}
status
createdAt
flag
total
}
}
`;
So essentially I am seeing all products, but with quantity of 4 for each. How can I fix this?
Change fetch policy
fetchPolicy: "no-cache",
also check if you are updating this query result after new order placing mutation (if you are it may cause updating with wrong values )
if you set fetchPolicy: "no-cache" you don't have to update query result after mutation

Can't find cache key in Cache redirect

When I start my react-native app I wan't to query "everything" for an offline experience.
So I
query all {
groups {
...GroupF
}
persons {
...PersonF
}
}${PERSON_ITEM}${GROUP_ITEM}
PersonF and GroupF are fragments.
The first view has a list of groups, each person can belong to a group. When the user clicks on an group the query looks like:
persons($group: ID) {
persons(group: $group) {
...PersonsF
}
}${PERSON_ITEM}
But my cacheRedirects just does not reflect the same data as is returned.
I know this because i console.log out the response in my component wrapper (it looks excellent here)
but in my
const cache = new InMemoryCache({
cacheRedirects: {
Query: {
persons: (_, args, {getCacheKeys}) => {
// I have tried everything here but nothing maps correctly
// I have tried getCacheKey({__typename: 'Person', id: args.group})
// I have tried following the apollo documentation
// No luck, it just can't find the group
// Using the chrome dev tools I don't see persons having groups
const a = getCacheKey({__typename: 'Person', id: args.group});
// Console.log(a) is:
// {generated: false, id: "Person:9", type: "id", typename "Person"}
}
}
}
});
Do you have any suggestions on how I can write a proper cache redirects persons query?
Help really really really appreciated
|1| https://www.apollographql.com/docs/react/advanced/caching.html#cacheRedirect
This was caused by the fact we used the id field in the Person, since we stoped using the field it works perfectly.

GraphQL mutation on id field

I have a GraphQL schema called Car Manufacturer which has two fields, id and name. What I am trying to accomplish via GraphiQL is to insert several data into the schema but I need to mutate both the mentioned fields.
Is there a way to insert a desired value on the id field?
Why you want to modify the id? For using them as indexes? Like id: 1, id: 2 an so on? If is that so, you should have something like globalIdField from graphql-relay in my opinion. Not mutate these ids yourself.
For example:
import { GraphQLObjectType, GraphQLFloat } from 'graphql';
import { globalIdField } from 'graphql-relay';
export default new GraphQLObjectType({
name: 'Car anufacturer',
description: 'desc',
fields: () => ({
id: globalIdField('CarManufacturer'),
title: {
type: GraphQLString,
resolve: async obj => obj.title,
},
}),
});
This way you will have unique ids and you can turn these ids on an ObjectIdfor use with MongoDB for example.
If you want to change this values for whatever other reason, you have to create Mutations just like #arian said
I hope that I helped you someway :)

After a mutation, how do I update the affected data across views? [duplicate]

This question already has an answer here:
Auto-update of apollo client cache after mutation not affecting existing queries
(1 answer)
Closed 3 years ago.
I have both the getMovies query and addMovie mutation working. When addMovie happens though, I'm wondering how to best update the list of movies in "Edit Movies" and "My Profile" to reflect the changes. I just need a general/high-level overview, or even just the name of a concept if it's simple, on how to make this happen.
My initial thought was just to hold all of the movies in my Redux store. When the mutation finishes, it should return the newly added movie, which I can concatenate to the movies of my store.
After "Add Movie", it would pop back to the "Edit Movies" screen where you should be able to see the newly added movie, then if you go back to "My Profile", it'd be there too.
Is there a better way to do this than holding it all in my own Redux store? Is there any Apollo magic I don't know about that could possibly handle this update for me?
EDIT: I discovered the idea of updateQueries: http://dev.apollodata.com/react/cache-updates.html#updateQueries I think this is what I want (please let me know if this is not the right approach). This seems better than the traditional way of using my own Redux store.
// this represents the 3rd screen in my picture
const AddMovieWithData = compose(
graphql(searchMovies, {
props: ({ mutate }) => ({
search: (query) => mutate({ variables: { query } }),
}),
}),
graphql(addMovie, {
props: ({ mutate }) => ({
addMovie: (user_id, movieId) => mutate({
variables: { user_id, movieId },
updateQueries: {
getMovies: (prev, { mutationResult }) => {
// my mutation returns just the newly added movie
const newMovie = mutationResult.data.addMovie;
return update(prev, {
getMovies: {
$unshift: [newMovie],
},
});
},
},
}),
}),
})
)(AddMovie);
After addMovie mutation, this properly updates the view in "My Profile" because it uses the getMovies query (woah)! I'm then passing these movies as props into "Edit Movies", so how do I update it there as well? Should I just have them both use the getMovies query? Is there a way to pull the new result of getMovies out of the store, so I can reuse it on "Edit Movies" without doing the query again?
EDIT2: Wrapping MyProfile and EditMovies both with getMovies query container seems to work fine. After addMovie, it's updated in both places due to updateQueries on getMovies. It's fast too. I think it's being cached?
It all works, so I guess this just becomes a question of: Was this the best approach?
The answer to the question in the title is
Use updateQueries to "inform` the queries that drive the other views that the data has changed (as you discovered).
This topic gets ongoing discussion in the react-apollo slack channel, and this answer is the consensus that I'm aware of: there's no obvious alternative.
Note that you can update more than one query (that's why the name is plural, and the argument is an object containing keys that match the name of all the queries that need updating).
As you may guess, this "pattern" does mean that you need to be careful in designing and using queries to make life easy and maintainable in designing mutations. More common queires means less chance that you miss one in a mutation updateQueries action.
The Apollo Client only updates the store on update mutations. So when you use create or delete mutations you need to tell Apollo Client how to update. I had expected the store to update automatically but it doesn’t…
I have founded a workaround with resetStore just after doing your mutation.
You reset the store just after doing the mutation. Then when you will need to query, the store is empty, so apollo refetch fresh data.
here is the code:
import { withApollo } from 'react-apollo'
...
deleteCar = async id => {
await this.props.deleteCar({
variables: { where: {
id: id
} },
})
this.props.client.resetStore().then(data=> {
this.props.history.push('/cars')
})
}
...
export default compose(
graphql(POST_QUERY, {
name: 'carQuery',
options: props => ({
fetchPolicy: 'network-only',
variables: {
where: {
id: props.match.params.id,
}
},
}),
}),
graphql(DELETE_MUTATION, {
name: 'deleteCar',
}),
withRouter,
withApollo
)(DetailPage)
The full code is here: https://github.com/alan345/naperg
Ther error before the hack resetStore

Resources