Hey,
Intended outcome:
Actual outcome:
Network error: Missing field state while computing key fieldsIn the next slide in the presentation(https://youtu.be/n_j8QckQN5I?t=1381), there is a solution for this issue -- setting keyFields: false on the SearchResults typePolicy, but I don't see how I can apply it my code without really, really complicating things. I feel like what I need is to be able to nest typePolicies (??? I really feel lost though, this is just a guess.)
Like this for example:
typePolicies: {
Query: {
fields: {
theQueryThatIsSadlyErroring: {
fields: {
theEntityThatIAmNotRequestingAllTheKeyingFieldsFor: {
keyFields: false
}
}
}
}
}
}
To me the above code would mean, "don't normalise entities when they come as part of this specific query".
Any help or direction is extremely appreciated, thank you!
What's holding you back from requesting the fields that are needed for computing the ID of the object?
If we had a good way of using keyFields to automatically add those fields to the query (like we do with __typename), I would implement that in a heartbeat, but it's hard to know which objects are going to have which __typenames just by looking at the original query, so instead we throw an exception when you try to store an object in the cache without all the fields that you indicated were important via keyFields.
As for the nesting idea, if theQueryThatIsSadlyErroring field retrieves an object that has a __typename, you can define a separate property in the top-level typePolicies object that describes that type, with its own keyFields and fields. In short, you can think of __typenames as class names, and fields as members/methods of those classes. As long as you know the __typename for everything (which you can, because __typename is always defined for every GraphQL object type), you shouldn't have to think about the relationship between a field and its grandparent or great-grandparent object鈥攐nly its parent object.
Hi Ben,
Thanks for answering so quickly and so comprehensively again!
What's holding you back from requesting the fields that are needed for computing the ID of the object?
The fields that are needed for computing the ID are nested fields. It is not that I don't just need these nested fields, it's that I don't need the object they're a part of at all.
typePolicies: {
Apple: {
keyFields: ['platonic', ['id'], 'state', ['id']]
},
Banana: {
keyFields: ['platonic', ['id'], 'state', ['id']]
},
AppleBanana: {
keyFields: ['platonic', ['id'], 'state', ['id']]
},
}
{
sadlyErroringQuery { # returns an array of objects of the type `AppleBanana`.
platonic {
id
}
state {
id
}
apple {
platonic {
id
}
state {
id
title
}
}
banana {
platonic {
id
}
# Not requesting state object and field that key Banana
}
}
}
If I change it to the following, it doesn't error:
{
nonErroringQuery {
platonic {
id
}
state {
id
}
apple {
platonic {
id
}
state {
id
title
}
}
banana {
platonic {
id
}
state { #<- requesting object and field that key Banana
id
}
}
}
}
if
theQueryThatIsSadlyErroringfield retrieves an object that has a __typename, you can define a separate property in the top-level typePolicies object that describes that type, with its own keyFields and fields. In short, you can think of __typenames as class names, and fields as members/methods of those classes. As long as you know the __typename for everything (which you can, because __typename is always defined for every GraphQL object type), you shouldn't have to think about the relationship between a field and its grandparent or great-grandparent object鈥攐nly its parent object.
If I understand you correctly, I think you mean doing something like this:
{
sadlyErroringQuery {
# returns object of the type sadlyErroringQueryResults
someOtherField
appleBananas {
platonic {
id
}
state {
id
}
apple {
platonic {
id
}
state {
id
title
}
}
banana {
platonic {
id
}
# Not requesting state object and field that key Banana
}
}
}
}
If I did this, I would do what you did in the presentation for the typePolicy, e.g.
sadlyErroringQueryResults: {
keyFields: false
}
I am sorry, I still don't understand how this relates to "As long as you know the __typename for everything (which you can, because __typename is always defined for every GraphQL object type), you shouldn't have to think about the relationship between a field and its grandparent or great-grandparent object鈥攐nly its parent object."
I don't know if this helps, but it's not so much that I need the object to take this shape -- I just need this little bit of data, and I like the shape. So, I could change it from being appleBanana.banana.platonic.id to appleBanana.bananaPlatonicId, or appleBanana.bananaPlatonic.id
Thank you for taking the time, I am sorry if it is just me being confused!! :)
I would strongly recommend requesting any fields that are involved in keyFields, even if you never use those fields explicitly in your own code.
To make that simpler, you could use a fragment:
query {
someRootQueryField {
...PlatonicStateIdFragment
apple {
...PlatonicStateIdFragment
state {
title
}
}
banana {
...PlatonicStateIdFragment
}
}
}
fragment PlatonicStateIdFragment on AppleBanana {
platonic { id }
state { id }
}
Given the above query, your typePolicies example code looks fine to me.
If your query omits information that's used by keyFields, the cache won't know how to compute an ID for the object, and may throw an exception. Passing keyFields: false prevents the cache from trying to compute an ID, but it also means the object will be embedded directly within its parent object, so you won't be able to benefit from getting new information about that object from different queries over time. Note that keyFields only works for objects that have __typename fields, not for individual fields within objects.
Thank you so much for the great advice. I have just done a major refactor with fragments :)
I'll close this off since there isn't an outstanding issue. Thanks for the discussion all.
What's holding you back from requesting the fields that are needed for computing the ID of the object?
Hundreds of queries :(
In my case the field I need for keyFields is the same for every Node resource, is there a way to configure Apollo to always add this field, like __typename?
Most helpful comment
Hundreds of queries :(
In my case the
fieldI need forkeyFieldsis the same for every Node resource, is there a way to configure Apollo to always add this field, like__typename?