-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[AC3] Hard to migrate to v3 due to lots of queries with non-normalized data type #6808
Comments
I ran into the same difficulties here when trying to migrate a large codebase, and held off on the upgrade for now. Specifying and implementing merging across hundreds of fields sounds very cumbersome, and I feel like I still don't have a good grasp on the cache mechanics even after going through the docs numerous times. Especially when it comes to implementing various pagination mechanisms (sometimes on the same field) I feel quite lost. I think a flag like the one above might be a bit too optimistic, but perhaps a way to specify a list of types that can be auto-merged? Or is this possible already today? |
We've been considering supporting an API for specifying type and field policies dynamically, as a fallback for types/fields that you haven't explicitly configured. Here's what I've got so far: new InMemoryCache({
typePolicies: {
Person: <explicit type policy for Person>,
Book: <explicit type policy for Book>,
...
policyForType(typename: string): TypePolicy | undefined {
// Return a (partial) type policy for the given typename.
// This function will be called only once per typename.
// Note that the returned TypePolicy may have a policyForField method.
},
Query: {
fields: {
someField: <explicit field policy for Query.someField>,
...
},
// Like policyForType, this function is called at most once per Type.field,
// and specifies fallback field policy properties (keyArgs, read, merge) for
// the specified field. Any type policy can have a policyForField function.
policyForField(fieldName: string): FieldPolicy | undefined {
// How you implement isMergeableQueryField is up to you.
if (isMergeableQueryField(fieldName)) {
return { merge: true };
}
},
},
},
}) There are lots of details to work out regarding the precedence of explicit configuration over dynamic configuration, and how to merge multiple policies for the same type/field, but I believe all those questions have reasonable answers. @chenesan @pleunv Does this kind of system sound like it might make things easier for you? |
One thing that TypeScript makes difficult: |
Looks good for our use case, Thanks for the great work @benjamn! Just have some questions:
policyForType: () => ({
policyForField: () => ({
merge: true,
})
})
|
Configuring Fields that don't have a custom |
Got it. So we also have to handle array case by ourselves, but at least we only need to write once with this new API 😄 Thanks for the work! |
We're running into the same issues. I posted a related question in Spectrum. |
Running into a similar issue with a lot of different objects, most of them have a "price" field (with a VAT, dutyFree and allTaxesIncluded value). They do not have an id and specifying a merge function for 50+ properties on big projects seems really error prone with AC3 at the moment :/ More details in here |
Motivating discussion: #6949 (comment) JavaScript developers will be familiar with the idea of inheritance from the `extends` clause of `class` declarations, or possibly from dealing with prototype chains created by `Object.create`. In a more general sense, inheritance is a powerful code-sharing tool, and it works well for Apollo Client for several reasons: 1. InMemoryCache already knows about the supertype-subtype relationships (interfaces and unions) in your schema, thanks to possibleTypes, so no additional configuration is necessary to provide that information. 2. Inheritance allows a supertype to provide default configuration values to all its subtypes, including keyFields and individual field policies, which can be selectively overridden by subtypes that want something different. 3. A single subtype can have multiple supertypes in a GraphQL schema, which is difficult to model using the single inheritance model of classes or prototypes. In other words, supporting multiple inheritance in JavaScript requires building a system something like this one, rather than just reusing built-in language features. 4. Developers can add their own client-only supertypes to the possibleTypes map, as a way of reusing behavior across types, even if their schema knows nothing about those made-up supertypes. Inheritance is a step back (in terms of freedom/power) from the completely dynamic policyForType and policyForField functions I proposed in #6808 (comment), but I think inheritable typePolicies will go along way towards addressing the concerns raised in #6808.
In my case I am using Apollo as a way to build a large cache of data from a device as the user navigates through pages. None of this data is normalised, and I was looking for a solution which behaved the same as My solution came in the form of using import introspection from "./__generated__/introspection.json";
const cache = new InMemoryCache({
typePolicies: Object.fromEntries(
introspection.__schema.types
.filter(({ kind }) => kind === "OBJECT")
.map(({ name }) => [name, { merge: true }])
),
});
Because this filters for only @chenesan could you try this in your codebase? Works great for me! |
Thank you @freshollie! Looks like a possible solution. |
It doesn't sound like there is an outstanding issue here, so closing. Thanks! |
I find it quite disapointing to close this issue on the sole basis that's it's not "outstanding" and with no real official workaround yet. It's still blocking people from migrating to the last version of this awesome library 😞 |
@Orodan can you be specific about what's outstanding here? As mentioned in #6808 (comment), we merged 2 PR's to help with this. Would you mind opening a new issue with the details that you feel are still outstanding, so it's easier for us to track (and ultimately resolve)? Thanks! |
I still think it would be nice if we have an option as @chenesan suggested from the first place, in large project we have graphtypes that do not have an id as the author shown in example, even we follow the possibleTypes approach still now we have to maintain the json file. Even if we have the auto genration for the json file still the logic to determine which graphtype to merge cannot be explicit |
Thank you for your work on AC3. The new declarative api like
possibleTypes
is great!For the better api our team would like to migrate from AC2 to AC3, but found out it's hard -- because it's tedious to configure the merging behavior on AC3 when our application have lots of non-normalized data type.
We know that in AC3 the InMemoryCache will replace, rather than auto merging incoming data. Because, as doc and #5603 has said, the auto merging behavior in some cases may be incorrect. To solve the problem, either we have to setting
keyFields
for each non-normalized data type (#6663), or settingmerge: true
on each field (as conclusion in doc).But we have lots of feature entry point and sub field is non-normalized, the data graph may be like:
The
featureX
for us is like an entry point with non normalized object, and often we want to queryfeatureX
with different fields:To fix the warning for each query / type, as a result we will have to manually write hundreds of lines of code, just to handle the merging behavior. This is a main issue that our team cannot easily migrate from AC2 to AC3. I've also seen issue like #6794 complaining about this.
So, is there any better way to handle this in such situation? (I guess the
merge: true
is for this kind of simplification, but still not good enough).Some suggestion
From my view, if it's possible, it might be good to bring the auto-merging behavior back as an option on
InMemoryCache
, maybe something like:Yes we all know it's not theoretically safe. But in our team's experience with AC2, we didn't have any issue caused by the auto merging behavior. Also, if it indeed cause bug on some types in user code, we can let it respect the custom merge function to correct it.
To prevent misusing this, the default can be
false
and we can make the option remind user to use it carefully and consciously, likedangerouslyAutoMergingDenormalizedObject: 'YES_I_UNDERSTOOD'
.Thank you for reading this long issue :)
The text was updated successfully, but these errors were encountered: