I call graphql to get a membership table layout with the following query:
query ($ids: [String]!, $countryCode: String!) {
flows(ids: $ids, countryCode: $countryCode) {
table {
id
title
blocks {
name
id
plans {
id
initial
level
media
name
recurring
recurring_period
trial_length
type
restrictions {
id
typeId
type
value
}
}
}
}
}
}
And when I look in the network tab I see this (which is correct):
https://gist.github.com/charles-mathieus-jomedia/28ea24688c9163c364739a6e6b36bb1b
TL;DR
flow: {
tables: [
{
blocks: [
{ id: 'audiobooks', name: 'block1' },
{ id: 'all', name: 'block2' }
{ id: 'family', name: 'block3' }
]
},
{
blocks: [
{ id: 'audiobooks', name: 'block4' },
{ id: 'all', name: 'block5' }
{ id: 'combo', name: 'block6' }
]
},
]
}
Intended outcome:
I get the right blocks when I remove the ids from the block in the query:
https://gist.github.com/charles-mathieus-jomedia/05c3de397de2ddcfadf06f5914035337
TL;DR
flow: {
tables: [
{
blocks: [
{ id: 'audiobooks', name: 'block1' },
{ id: 'all', name: 'block2' }
{ id: 'family', name: 'block3' }
]
},
{
blocks: [
{ id: 'audiobooks', name: 'block4' },
{ id: 'all', name: 'block5' }
{ id: 'combo', name: 'block6' }
]
},
]
}
Actual outcome:
When I leave the ids, I get the wrong blocks:
https://gist.github.com/charles-mathieus-jomedia/2d1e61404a944fb9a2618512056774d5
TL;DR
flow: {
tables: [
{
blocks: [
{ id: 'audiobooks', name: 'block1' },
{ id: 'all', name: 'block2' }
{ id: 'family', name: 'block3' }
]
},
{
blocks: [
{ id: 'audiobooks', name: 'block1' },
{ id: 'all', name: 'block2' }
{ id: 'combo', name: 'block6' }
]
},
]
}
How to reproduce the issue:
Use the same id in different nested array of objects,
Version
Sorry in advance if this post is very long, but I didn't filter the response to help you guys see what's happening
Thanks @charles-mathieus-jomedia ! Absolutely same issue just got me really hard. When I query my graphQL server results are perfect. But it seems that Apollo just takes prev values from cache and inserts them for every similar id. Someone explain the issue please.
@pavship Yes I feel like this is not an error, and was built on purpose somehow... However, if there is no way to "fix" this quickly it should at least be documented explicitly that apollo works this way!
I'm seeing this too. It is completely breaking my app and I don't see a way to fix it. I need the ids! GraphQL seems unusuable :(
Actually I was able to fix by setting fetchPolicy to no-cache
. It's confusing what the difference is between this and network-only
which I was using before. I think it's a terrible decision to have the default be neither of these. I was very confused without network-only
set before I'd make graphql calls and they would just not return. Turns out they were cached...which I'd expect to still return data from the call! Worst part is the id field that is causing wrong data for me is not even defined as an ID type in my schema!!! It's just a String that happens to be called id
.
@simplychaos I think you might be right!
Sometimes it makes sense to not use the cache for a specfic operation. This can be done using either the network-only or no-cache fetchPolicy. The key difference between these two policies is that network-only still saves the response to the cache for later use, bypassing the reading and forcing a network request. The no-cache policy does not read, nor does it write to the cache with the response. This may be useful for sensitive data like passwords that you don’t want to keep in the cache.
I think it should be better documented how the cache works or maybe be able to set an other "primary key" for the cache
I have the same issue. when response has duplicate ID's. Apollo-client parse it wrong. it return any one data collection, for duplicate ids.
I have encountered the same bug, my current workaround/hack is to set a random ID for the affected types (effectivly turning off caching). Since dataIdFromObject
only receives the object itself and not the whole "context" (like how it is nested) I dont see any option to do a better workaround.
const cache = new InMemoryCache({
dataIdFromObject: object => {
// FIXME: workaround buggy apollo cache, dont cache certain types at all!
switch (object.__typename) {
case 'Category':
return Math.random()
default:
return defaultDataIdFromObject(object) // fall back to default handling
}
}
})
@hwillson (and the other maintainers)
Also, I have extracted a minimal reproduction:
I also recommend to check the following article about this issue
https://kamranicus.com/posts/2018-03-06-graphql-apollo-object-caching
It does not seem like this bug is actively worked on, but in any case I migrated the endpoint from my example above from (deprecated) apollo launchpad to codesandbox. I upgraded all dependencies and confirmed it is still broken.
I just ran into this issue.
{ ..., id: 'some-id', isPrimary: true }
.{ ..., id: 'some-id', isPrimary: null }
.Following the tip from @simplychaos to change fetchPolicy
from network-only
to no-cache
solves my problem.
For reference, I am using apollo-angular
and InMemoryCache
.
I had the same issue with an endpoint returning multiple nodes with an identical "id" field. The workaround by @eins78 worked for me, while setting the fetchPolicy
to no-cache
in my Query
React component didn't - the component kept receiving null
data.
Thanks for reporting this. There hasn't been any activity here in quite some time, so we'll close this issue for now. If this is still a problem (using a modern version of Apollo Client), please let us know. Thanks!
The bug is still present in current version of Apollo Client. It still leads to data corruption when used as documented.
I updated all packages to latest version in the reproduction: https://codesandbox.io/s/0y9w819z0n
@eins78 Thanks for the reproduction. This isn't a bug. Add __typename
to the posts
selection set in your query, and it will work:
query blogPostArchives {
years {
id
name
categories {
id
name
posts {
id
title
__typename
}
}
}
}
When you don't provide a __typename
, Apollo's in-memory cache uses a fallback method of associating data in the cache when it normalizes everything (you can read more about this here). This fallback isn't foolproof and in your case when the data is normalized then attempted to be retrieved, the first set of posts
will always be returned. But when you provide a __typename
the cache is able to use that data to properly differentiate between the different posts
, so when retrieving everything works properly.
The solution with __typename
doesn't work for me. But this helped me:
import { InMemoryCache, defaultDataIdFromObject } from 'apollo-cache-inmemory';
const cache = new InMemoryCache({
dataIdFromObject: object => {
switch (object.__typename) {
case 'foo': return object.key; // use `key` as the primary key
case 'bar': return `bar:${object.blah}`; // use `bar` prefix and `blah` as the primary key
default: return defaultDataIdFromObject(object); // fall back to default handling
}
}
});
It is also documented here: https://www.apollographql.com/docs/react/advanced/caching/#normalization
@websolutions-hamburg For sure; if id
/ _id
/ __typename
aren't enough, then dataIdFromObject
can be used to manually override things.
@hwillson No, adding __typename
does not fix it: https://0y9w819z0n.codesandbox.io/
The problem (seemingly) is the way the data is nested: the Posts are nested inside a Category, those are nested inside Year. The cache then considers all Categories of the same ID "equal" and thus returns the same list of Posts. But, because Category is nested in a Year, they are not actually the same.
Yes, this is a weird data structure, but its allowed by the spec and it resulted from a real-world use case.
As stated above, the only workaround we've found is to build a special ID just for the apollo cache, so a Category would have a "cacheKey" of ${YearID}--${category}
.
As stated above, the only workaround we've found is to build a special ID just for the apollo cache, so a Category would have a "cacheKey" of
${YearID}--${category}
.
Because this is a very confusing thing to explain, I forked the reproduction and added the workaround from our app:
server: https://codesandbox.io/s/apollonestedcachingbug-u46hq
client: https://codesandbox.io/s/basic-apollo-app-etgv0
Hello All , is there any solution for this issue? i am using apollo 2.6 and still see the same issue mentioned above.
I ran into this bug as well. When an item nested under one entity changed, the same item (same id) nested under a different entity would change as well when it was not supposed to.
Our workaround was to overwrite the original id with a unique ID on the incoming data (combined the entity id and original id in this case), then re-instate the original ID when passing it back to the database to be updated. Hacky, but works.
You could also implement a custom dataIdFromObject and return null
for types that should not be normalized in the cache
I got the exact same problem with this bug. My graphql is exposing objects like {id: "anyId", value: "anyValue"}
.
My workaround is to rename the field id
by something else.
I lost few hours because of that, glad to see there is an explanation in the documentation (https://www.apollographql.com/docs/react/caching/cache-configuration/#assigning-unique-identifiers)
Also lost hours because of this issue :(
This should not be closed!
I'm losing countless hours over this. This was reported years ago and still not fixed. No way this should have been closed
Sadly I have to report that this is still an issue with v3 of apollo client.
Updated Sandbox (using latest versions of all dependencies): https://codesandbox.io/s/apollo-caching-bug-v3-zlf2m
Why is this closed? I am having the same issues...
Hi all - this issue is still closed as there isn't a bug here. This is the way the Apollo Client cache works - by default it treats entities with the same __typename:id
as being the same objects, when it splits things out for normalization. Looking at the graphql response from the repro:
{
"data": {
"years": [
{
"id": "y1",
"name": "2000",
"__typename": "Year",
"categories": [
{
"id": "c1",
"name": "cat 1",
"__typename": "Category",
"posts": [
{
"id": "p1",
"title": "post 1",
"__typename": "Post"
},
{
"id": "p2",
"title": "post 2",
"__typename": "Post"
}
]
},
{
"id": "c2",
"name": "cat 2",
"__typename": "Category",
"posts": [
{
"id": "p3",
"title": "post 3",
"__typename": "Post"
},
{
"id": "p4",
"title": "post 4",
"__typename": "Post"
}
]
}
]
},
{
"id": "y2",
"name": "2001",
"__typename": "Year",
"categories": [
{
"id": "c1",
"name": "cat 1",
"__typename": "Category",
"posts": [
{
"id": "p5",
"title": "post 5",
"__typename": "Post"
},
{
"id": "p6",
"title": "post 6",
"__typename": "Post"
}
]
},
{
"id": "c2",
"name": "cat 2",
"__typename": "Category",
"posts": [
{
"id": "p7",
"title": "post 7",
"__typename": "Post"
},
{
"id": "p8",
"title": "post 8",
"__typename": "Post"
}
]
}
]
}
]
}
}
Even though the categories
are returned under 2 different years, they are still using the same id
's of c1
and c2
. When Apollo Client splits this response up to store in its cache, it splits out all of the entities by __typename
and id
. So when things are stored in the cache, they look like this:
The c1
and c2
categories are only stored once, since they are seen as being the same entities due to their __typename:id
.
That being said, if you don't want this to happen and you're using Apollo Client 3, you can disable normalization for Category
types. This will make sure each category is seen as being independent, and is stored under each year. As a quick example, if you change the repro to include the following type policy:
const client = new ApolloClient({
uri: "https://l72oyj30r7.sse.codesandbox.io/graphql",
cache: new InMemoryCache({
typePolicies: {
Category: {
keyFields: false
}
}
})
});
and re-run the repro and check the cache, you'll now see:
Categories are no longer normalized, which means the duplicate categories exist under their associated years. You'll see that the posts are still normalized, with p1
to p8
being split out as their own entities.
Hopefully this helps clear things up. Thanks!
Is it possible to make Apollo smarter? If I query 10 out of 10 fields on an
object and then later only query 3 out of 10, can Apollo understands that
the second query was only partial and not replace the objects but update
the fields should some values change?
On Tue, Aug 4, 2020, 2:49 AM Hugh Willson notifications@github.com wrote:
Hi all - this issue is still closed as there isn't a bug here. This is the
way the Apollo Client cache works - by default it treats entities with the
same __typename:id as being the same objects, when it splits things out
for normalization. Looking at the graphql response from the repro:{
"data": {
"years": [
{
"id": "y1",
"name": "2000",
"__typename": "Year",
"categories": [
{
"id": "c1",
"name": "cat 1",
"__typename": "Category",
"posts": [
{
"id": "p1",
"title": "post 1",
"__typename": "Post"
},
{
"id": "p2",
"title": "post 2",
"__typename": "Post"
}
]
},
{
"id": "c2",
"name": "cat 2",
"__typename": "Category",
"posts": [
{
"id": "p3",
"title": "post 3",
"__typename": "Post"
},
{
"id": "p4",
"title": "post 4",
"__typename": "Post"
}
]
}
]
},
{
"id": "y2",
"name": "2001",
"__typename": "Year",
"categories": [
{
"id": "c1",
"name": "cat 1",
"__typename": "Category",
"posts": [
{
"id": "p5",
"title": "post 5",
"__typename": "Post"
},
{
"id": "p6",
"title": "post 6",
"__typename": "Post"
}
]
},
{
"id": "c2",
"name": "cat 2",
"__typename": "Category",
"posts": [
{
"id": "p7",
"title": "post 7",
"__typename": "Post"
},
{
"id": "p8",
"title": "post 8",
"__typename": "Post"
}
]
}
]
}
]
}
}Even though the categories are returned under 2 different years, they are
still using the same id's of c1 and c2. When Apollo Client splits this
response up to store in its cache, it splits out all of the entities by
__typename and id. So when things are stored in the cache, they look like
this:[image: Image 2020-08-03 at 2 39 53 PM png]
https://user-images.githubusercontent.com/137740/89215800-430e2580-d597-11ea-8f24-6fb5992bd627.pngThe c1 and c2 categories are only stored once, since they are seen as
being the same entities due to their __typename:id.That being said, if you don't want this to happen and you're using Apollo
Client 3, you can disable normalization for Category types. This will
make sure each category is seen as being independent, and is stored under
each year. As a quick example, if you change the repro to include the
following type policy:const client = new ApolloClient({
uri: "https://l72oyj30r7.sse.codesandbox.io/graphql",
cache: new InMemoryCache({
typePolicies: {
Category: {
keyFields: false
}
}
})});and re-run the repro and check the cache, you'll now see:
[image: Image 2020-08-03 at 2 48 14 PM png]
https://user-images.githubusercontent.com/137740/89216449-671e3680-d598-11ea-8830-ce4524e4b392.pngCategories are no longer normalized, which means the duplicate categories
exist under their associated years. You'll see that the posts are still
normalized, with p1 to p8 being split out as their own entities.Hopefully this helps clear things up. Thanks!
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/apollographql/apollo-client/issues/3234#issuecomment-668184108,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAGEPFZNJLAAK57OZRKBAWLR64BDBANCNFSM4EYECU5A
.
--
http://www.filevine.com/catalyst-summit/
--
This E-mail and any attachments hereto are confidential, remain the
property of Filevine, Inc. ("Filevine"), and are intended solely for the
use of the individual or entity to whom they are addressed. If you are not
the intended recipient, you may not disseminate, distribute, copy, or take
any action in reliance on the contents of this E-mail. Please notify the
sender immediately if you have received this E-mail by mistake and delete
it from your system. Although Filevine has taken reasonable precautions to
ensure that no viruses are present in this E-mail, we are not responsible
for any loss or damage arising from this E-mail or attachments. Nothing in
this message may be relied upon for the purposes of creating, amending,
and/or abrogating any contract, or any clause or provision of an existing
agreement.
@jmsunseri The way to make Apollo smarter is to tell the InMemoryCache
how to compute an ID for your objects, using keyFields
or dataIdFromObject
. This allows object fields to be safely merged, when (and only when) object identities match, which is exactly what you're saying you want.
Object identity is fundamentally an application-level concern, so it's not possible/appropriate for Apollo Client to pretend that objects with unknown identities might be the same object. Identity is something you (the application developer) must specify, using the tools that we've given you. The good news is that you only have to capture this information in one place, in the options you pass to the InMemoryCache
constructor.
I don't think that's what I'm trying to say. Each object has an id prop
and type and Apollo correctly identifies that I'm talking about the same
object. The problem is that some screens and some calls require less
information than others and Apollo instead of just adding or updating an
object will replace and object thus affecting other components that were
relying on that cache
On Tue, Aug 4, 2020 at 11:59 PM Ben Newman notifications@github.com wrote:
@jmsunseri https://github.com/jmsunseri The way to make Apollo smarter
is to tell the InMemoryCache how to compute an ID for your objects, using
keyFields or dataIdFromObject. This allows object fields to be safely
merged, when (and only when) object identities match, which is exactly what
you're saying you want.Object identity is fundamentally an application-level concern, so it's not
possible/appropriate for Apollo Client to pretend that objects with unknown
identities might be the same object. Identity is something you (the
application developer) must specify, using the tools that we've given you.
The good news is that you only have to capture this information in one
place, in the options you pass to the InMemoryCache constructor.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/apollographql/apollo-client/issues/3234#issuecomment-668681908,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAGEPF53AX5NVNUTOHL2KNTR7AV7XANCNFSM4EYECU5A
.
--
Thanks,
Justin Sunseri
Software Developer
Filevine
--
http://www.filevine.com/catalyst-summit/
--
This E-mail and any attachments hereto are confidential, remain the
property of Filevine, Inc. ("Filevine"), and are intended solely for the
use of the individual or entity to whom they are addressed. If you are not
the intended recipient, you may not disseminate, distribute, copy, or take
any action in reliance on the contents of this E-mail. Please notify the
sender immediately if you have received this E-mail by mistake and delete
it from your system. Although Filevine has taken reasonable precautions to
ensure that no viruses are present in this E-mail, we are not responsible
for any loss or damage arising from this E-mail or attachments. Nothing in
this message may be relied upon for the purposes of creating, amending,
and/or abrogating any contract, or any clause or provision of an existing
agreement.
Adding this is the only thing that really helped:
# Disable Apollo caching for this sellerProducts
__typename @skip(if: true)
Which makes sense, since i was using the same typename
and apollo was having trouble with the caching...
The other methods did not help me, since the problem was not the caching, but using the same typename.
I would not recommend changing the caching policy unless you really need to!
Is it possible to make Apollo smarter? If I query 3 out of 10 fields on an
object and then later only query 10 out of 10, can Apollo understands that
the second query requires more fields and not replace the objects but update
the object with the new object.
I'm also facing this same problem.
Same problem for me. There should be a way to tell apollo client that the query response must be exactly as the network request data response. This is very frustrating.
Updating version from 2.2.1
to 2.4.4
fixed the issue for me
Most helpful comment
Actually I was able to fix by setting fetchPolicy to
no-cache
. It's confusing what the difference is between this andnetwork-only
which I was using before. I think it's a terrible decision to have the default be neither of these. I was very confused withoutnetwork-only
set before I'd make graphql calls and they would just not return. Turns out they were cached...which I'd expect to still return data from the call! Worst part is the id field that is causing wrong data for me is not even defined as an ID type in my schema!!! It's just a String that happens to be calledid
.