This currentUser
query was running too many times.
Types:
type UserDetailsType {
a: String
b: String
c: String
}
type User {
_id: ID!
details: UserDetailsType
}
type Query {
currentUser: User
}
The response always comes as
{
"__typename": "User",
"_id": "dDnw7wqhMPMeog2Y7",
"details" : {
"a" : "something",
"b" : null,
"c" : null
}
}
But the cache was getting updated weirldy. Sometimes those b
, c
fields would appear on the cache like "b" : null
. Then they would dissappear.
So, I tried to compare the existing and incoming like:
new InMemoryCache({
typePolicies: {
User: {
fields: {
details: {
merge(existing, incoming, { mergeObjects }) {
// ...
console.log('existing', existing);
console.log('incoming', incoming);
// ...
}
}
}
}
}
})
That incoming
parameter sometimes has missing fields. if those fields are null
!!! Thus, causing the cache to remove those null
fields.
But if I enable merge missing fields from incoming
don’t get removed from cache.
new InMemoryCache({
typePolicies: {
UserDetailsType: {
merge: true
}
}
})
How do I fix it without manually setting merge: true
on every possible fields/types?
Hey @Dulguun_Otgon 
What version of @apollo/client
are you using? This looks very similar to this issue which was fixed by this PR in v3.7.11. If you’re on a version < 3.7.11, try upgrading to at least that version and see if that helps.
I’m on version v3.7.14 and webpack module federation. I’ve tried to reproduce this issue with no success.
To clarify, when you say:
I’ve tried to reproduce this issue with no success.
Are you saying everything works as expected? Or that you’re still seeing the problem mentioned above but are having difficulty trying to create a reproduction of the issue?
I meant I failed to reproduce this issue. Seems like it’s not an apollo bug, but a bug from the project I’m working on.
Got it! Thanks for the clarification.
No clue what could be causing incorrect cache updates. There are no cache.updateQuery
or cache.writeQuery
calls that would update that particular field
Any query/mutation/subscription that would return that type with a fetchPolicy
set to something that would write to the cache could be triggering that cache update. It doesn’t have to be an explicit cache.updateQuery
or cache.writeQuery
call. Check around at the queries that are executing and see if any of them query for overlapping data (i.e. the currentUser
field). That might help you narrow down which queries might be competing with each other.
So, any type that has User
type field and returns wrong data for it could cause it? That makes sense. So it seems like more of a backend bug in the project that I’m working on.
Yep thats correct! And thats because Apollo uses a normalized cache.
Is it possible to disable normalization for some types on the server side?
For example, I want to disable it inside my schema definition (maybe by using some directive).
Cache normalization is a client-only feature so the server would have no knowledge that data is stored in this way on the client. You can however choose to disable data normalization altogether for certain types where it makes sense by setting the keyFields
value to false
new InMemoryCache({
typePolicies: {
User: {
keyFields: false
}
}
});
This may or may not make sense for you, but play around with it and see what kind of effect it has. If you haven’t installed it already, I’d highly recommend using the Apollo Client Devtools which will allow you to explore your cache. Take note of how the data is shaped/normalized and how disabling normalization affects how that data is stored.
If you’d prefer not to use dev tools and want to inspect it via code, you can dump the contents of the cache by calling its extract
function:
// view the entire contents of the cache
console.log(cache.extract())
Hope this helps!
I was hoping for some directive I could use on the schema definition, so that the client can take the hint and just merge them
Unfortunately something like that doesn’t currently exist. You’d need the merge: true
trick you originally mentioned.
I’ve managed to reproduce this behavior. I think it’s a bug. Why update and remove the fields that are not even selected?
Would you be able to provide a snippet of the queries, type policies, and what the cache looks like after the queries are run? I’m not quite sure the behavior you’re seeing otherwise.
For example type schema is:
type UserDetailType {
a: String
b: String
}
type User {
id: ID!
details: UserDetailType
}
type Post {
id: ID!
user: User!
}
type Query {
currentUser: User
post: Post
}
If I run this query first
query CurrentUser {
currentUser {
id
details {
a
b
}
}
}
The cache is correctly set.
If I run this query:
query Post {
post {
id
user {
id
details {
a
}
}
}
}
and the post’s user.id
is same as currentUser
query’s user, then it removes the details.b
field from cache.
After this if I run the currentUser
query, it has to send request to the server again. Because the selected details.b
field got removed from the cache, after running Post
query.
Is UserDetailType
a normalized type in the cache? In other words, do you see a { __ref: 'UserDetailType:...' }
value for that details
field, or the full type? I’m guessing it is not normalized since I don’t see an id
for that field in your schema.
The reason its getting replaced is because it doesn’t know how to handle merging that details
field on the User
type out of the box. By default, the cache assumes that any non-normalized field should be overwritten. The a look at the “Merging non-normalized objects” section in the docs and I think you’ll see that example given is almost identical to this one. You’ll need to define a merge
function for that field.
Hope this helps!
When you’re using document databases like MongoDB. A type without an id is very common. Writing merge for every nested type without and id is kinda pain in the ass. Wasn’t expecting this when I migrated from version 2 to version 3.