With GraphQL Apollo Client, How To set #client fields to store in Cache object? - graphql

I have an Apollo Query set as I have below.
query {
speakers {
id
name
checkBoxField #client
}
}
I have a field policy set as follows:
export const genericBoolean = cache.makeVar(false);
typePolicies: {
Speaker: {
fields: {
checkBoxField: {
read(checkBoxField = false) {
return genericBoolean === true ? "true" : "false";
...
I have a button on my page where I use useQuery defined and that button executes the code:
onClick : genericBoolean(true)
When I dump (JSON.stringify) the data returned from useQuery, the checkBoxField does change to true for all rows as expected, but in my Apollo Cache (as seen from chrome extension), there is no field stored for checkBoxField. I want that there so I can iterate through my cache and get the set valued for each row.

According to the Apollo docs, you can use reactive variables OR the Apollo client cache to store local state: https://www.apollographql.com/docs/react/local-state/managing-state-with-field-policies/#storing
Your example above is using reactive variables. To store your data in the cache you would need to use writeQuery or writeFragment: https://www.apollographql.com/docs/react/local-state/managing-state-with-field-policies/#storing-local-state-in-the-cache

Related

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

Why is my graphQL query returning results without `where` filters?

I am working on a webapp with a nuxt/vuetify/apollo frontend. The backend is a strapi (v3.0.0-beta.18) server with a graphQL endpoint and a mongoDB database (v4.2.2)
It could be a newbie question since it's my first graphql project. I have a query for a collection called tags. It looks as follows:
query Tags($search: String, $selected: [ID], $limit: Int) {
tags: tags(
limit: $limit
sort: "score:desc"
where: { name_contains: $search }
) {
id
name
description
type
}
selected: tags(where: { id_in: $selected }) {
id
name
description
type
}
}
That query is returning every result that should be filtered by the where object, while the sort and limit filters work. The behavior is the same in my frontend app and on the graphQL playground. Am I missing something?
Note that strapi Shadow CRUD feature is enabled.
It turns out that I missed one of the breaking changes when upgrading strapi to the latest version. The mongose connector changed its name: https://strapi.io/documentation/3.0.0-beta.x/migration-guide/migration-guide-beta.17-to-beta.18.html

Apollo graphQL - can you query local state using variables without having to use a resolver?

I am using apollo-cache-inmemory, apollo-client, react-apollo.
My local state contains a users array like so: -
users: [{
__typename: "User",
userId: "hashid1",
...
},
{
__typename: "User",
userId: "hashid2",
...
}]
Now I can obviously run a simple query to retrieve all the users from the local state: -
import gql from "graphql-tag"
export default gql`{users #client {userId}}`
However, what I would like to do is to be able to query the users array directly, passing variables like so: -
const userDetails = await client.query({ query: USER_DETAILS, variables: {id: "hashId1"}})
Is it possible to run this query without using a resolver? I have attempted the following but { data } returns as null: -
export default gql`query user($id: String!) {users(userId: $id) #client {userId}}`
I already use resolvers and can easily write one to take care of this issue but I am wondering if there it is possible to perform this task without one?
It looks like you're looking for some magic ;)
You must write customization code (overwrite default resolver - return all records) to have a customized behavior (return data filtered by your criteria). That should be obviuos.
There is no default/ready/built in searching/filtering syntax in graphql - therefore, there is no default behaviours for them in apollo-client (no matter local/remote server/data). It is up to you to implement what you need.

readQuery from cache results in Can't find field <field> on object undefined

I'm getting started with graphQL and apollo. I'm doing some simple queries and mutations fine but when I try to access data from apollo cache I keep getting "Can't find field me on object undefined" but when using apollo chrome plugin it returns data correct.
When logged into my app I run a simple query that fetches the user object from the server. It logs correct, I can see in chome dev apollo under queries and cache that my user data is there.
const ME_QUERY = gql`
query {
me {
_id
username
password
exchanges {
name
active
}
}
}
`;
<Query query={ME_QUERY}>
{({loading, error, data}) => {
if (error) {
return <Error error={error.message} />;
}
if (data) {
console.log(data, 'data');
}
return <div>asdf</div>
}}
</Query>
I have wrapped withApollo and I can log the client object fine in the component where I'm trying to query. I run the following code:
componentDidMount() {
console.log(this.props.client.readQuery, 'propps');
const user = this.props.client.readQuery({
query: gql`
{
me {
_id
username
password
exchanges {
name
active
}
}
}
`
});
console.log(user, 'user');
And that results in Uncaught Error: Can't find field me on object undefined Ive also tried with adding variables but it doesn't help.
Which object is undefined? Any idea why my query fails?
You are probably using Apollo Client > 2.5 which handles state locally.
Read the migration guide for more information.
Use the default client resolver to resolve your local state
Use cache.write to write default data into your cache. This is the part of the migration that causes your crash.

How can you prevent running a resolver based on the selection set?

Example:
query {
me {
starredPosts {
id
}
}
}
How can the server notice that only the ids are requested, and use the already-fetched user.starredPosts (an array of ids), instead of calling Posts.findOne(id) for each id?
We had the same problem and are in the process of open-sourcing the tools we've built out over the last year and a half internally to address these issues: https://github.com/4Catalyzer/graphql-node-resource/pull/1.
The solution we use is, for object resolvers like that, to resolve them to a "stub" object that contains only the ID, something like:
const childField = {
type: ChildType,
resolve: obj => ({ id: obj.childId }),
};
Then we use DataLoader to fetch the additional fields on the child objects when they're required by using our own default resolver.
We connect to our internal REST API, which supports batching on those requests, so queries that require additional fields get efficiently dispatched and resolved.
However, this does introduce potential for error when writing custom resolvers, as there's no guarantee that obj actually has the relevant fields. We've addressed this by setting up our static types to prevent unchecked access to properties of obj.
You can examine info.fieldNodes[0].selectionSet.selections or use graphql-fields package:
const postsIds = user.starredPosts
const selectionSet = Object.keys(graphqlFields(info))
const onlySelectingId = isEqual(['__typename', 'id'], selectionSet.sort())
if (onlySelectingId) {
return postIds.map(id => ({ id, __typename: 'Post' }))
} else {
return favs.map(id => Post.findOneById(id))
}

Resources