Migrating from Apollo Client 2 to 3
About 8 months ago apollo-client had a major release and we were excited to try out the new features especially the local fields and the reactive variables. While their migrating guide does an incredible job explaining new features and the reasoning behind the breaking changes, it didn't offer much help for those who liked and wanted to keep the old non-normalized merge inference. This is how we managed to migrate to version 3 while keeping our development flow the same.
The main breaking change is how apollo deals with nested objects without an ID. Before, it inferred an ID for those nested objects and would merge the results of other requests that queried it. For example, let's suppose we have in the same screen two queries for managing the notifications in our company: CompanySettingsForPraise
and CompanySettingsForFeedback
.
Previously apollo v2 would create a fake id for the nested field something like Company.1.CompanySettings
, so if we queried CompanySettingsForPraise
followed by CompanySettingsForFeedback
, apollo would merge the settings, and we would have the cache for the company as the following:
However, this merge strategy wasn't always safe, so in v3, the default is to replace the previous result when no merge strategy is defined. The result of the last queries without any additional configuration would then be:
⚠️ This was a problem for us since the place that used CompanySettingsForPraise
would be updated without the key praiseNotificationsEnabled
it had asked for earlier!
For us to be able to merge them, like we used to, we have to declare in the cache a merge strategy. This can be achieved by declaring a typePolicy
with merge: true
.
But that would mean that every new type we would have to remember (and I don't know you, but I forget things all the time) that it won't be merged by default and manually add the type to the cache configuration. This seemed like it would cause a number of unexpected and hard-to-track bugs as it is order dependent and will usually only impact longer sessions.
Hopefully, the Apollo team was aware of this difficulty and proposed an inheritance of typePolicies making it easier to extend multiple types at once. This means we can define a client-only supertype
; we chose the name MergeableTypes
, leaving our configuration like this:
Now we needed to discover how to define the mergeableTypesArray
.
We were already using graphql-code-generator fragment-matcher
to generate our IntrospectionFragmentMatcher
, which was replaced by possibleTypes
in v3. We saw a section about custom-plugins and decided to give it a try.
We wrote a very simple plugin that generates a typescript file with an array containing all the ObjectTypes
and InterfaceTypes
of our schema.
We added the custom plugin to our codegen.yml
And sure enough, now our types would behave just like in v2, merging the nested fields! The nice part is that if we ever need to overwrite the merge strategy for a specific field, we can just add it to the cache configuration. Our flow managed to stay the same, and we can now leverage the new features of local fields and reactive variables 🎉.
How was your experience migrating to v3? Doubts, comments, feedback? Leave it in the comments below!