graphql-code-generator-community
graphql-code-generator-community copied to clipboard
Apollo v4 CodeGen Broken w/o config Overrides
Which packages are impacted by your issue?
@graphql-codegen/typescript-react-apollo
Describe the bug
When using @graphql-codegen with the typescript-react-apollo plugin and @apollo/client v4 the generated code is invalid. Generated hooks (e.g. useQuery / useMutation) must be imported from @apollo/client/react, but are imported through @apollo/client. This causes a generated file to compile.
A workaround is to manually specify the following in the codegen.ts file:
config: {
apolloReactCommonImportFrom: "@apollo/client/react",
apolloReactHooksImportFrom: "@apollo/client/react",
}
Your Example Website or App
N/A
Steps to Reproduce the Bug or Issue
- Bump
@apollo/clientfrom v3 to v4 - Run
codegen - Build the generated code.
Expected behavior
The file does not compile and it is expected to compile.
Screenshots or Videos
No response
Platform
- OS: macOS
- NodeJS: v24
Codegen Config File
No response
Additional context
No response
Hello ! I encountered the same issue after upgrading to apollo v4 but I haven't succeed to resolve it like you describe. I got some other types with invalid import after code generation.
Type error: Namespace '"./node_modules/@apollo/client/react/index"' has no exported member 'MutationFunction'.
@ksylvest did you succeed to solve it ? Thanks
I really hope that a new version of the plugin will be release soon :)
@mchoraine I hadn't tested w/ a schema containing mutations. I just re-tested you are correct those types don't appear to exist anymore. I'm not sure how to solve.
Has anyone picked this up already? Bump @apollo/client from v3 to v4
Otherwise I can maybe take a look
Hello ! I encountered the same issue after upgrading to apollo v4 but I haven't succeed to resolve it like you describe. I got some other types with invalid import after code generation.
Type error: Namespace '"./node_modules/@apollo/client/react/index"' has no exported member 'MutationFunction'.@ksylvest did you succeed to solve it ? Thanks
I really hope that a new version of the plugin will be release soon :)
Query types have been moved into type namespaces; https://www.apollographql.com/docs/react/migrating/apollo-client-4-migration#namespaced-types
MutationFunction should be located in the useMutation namespace, you could try useMutation.MutationFunction.
Hi all 👋
Apollo Client v4 and generated hooks incompatibility goes beyond type renames. v4 introduces lots of type overloads that do NOT work with generated hooks, creating a real risk of runtime errors in production.
You can see an example of wrong type inference here.
Apollo team recommends against using generated hooks for this reason.
We (Codegen team) recommend migrating to Client Preset instead of relying generated hooks. We will deprecate typescript-react-apollo plugin soon.
Here's a codemod that you could use for migration: https://www.npmjs.com/package/@eddeee888/gcg-operation-location-migration . Please let me know if you have any issues.
- Blog post: https://the-guild.dev/graphql/hive/blog/graphql-codemod-use-case-migrating-from-apollo-client-generated-hooks-to-client-preset
- Video: https://www.youtube.com/watch?v=GGmt0hvnQNU
I don't think my team would be glad to review few thousand lines of changes with migration from codegen to client preset 😅.
In my case bundle size and strictness of dataState property isn't really an issue. Wouldn't want to stockpile techdebt either so would be nice to have some stopgap option where it generates with uncertain dataState. I'll try to look into it in spare time
Logic for visitor.ts doesn't seem that hard to upgrade, i've mostly updated it to support v4 https://gist.github.com/EvgeniyKumachev/c65fdd9092b1a1d09132f700774c238e
Since in v4 they removed hocs and components options for them should probably be deprecated or removed but i'm not sure about deprecating them myself
Hi @EvgeniyKumachev ,
I appreciate the time you put into trying to fix this. 🙂
I don't think my team would be glad to review few thousand lines of changes with migration from codegen to client preset 😅.
I totally understand the concern, I highly recommend doing this in multiple PRs.
You can target where you'd like to run the migrations by changing a generates blocks' key. Hopefully, there are tests in place to catch potential errors.
In my case bundle size and strictness of dataState property isn't really an issue. Wouldn't want to stockpile techdebt either so would be nice to have some stopgap option where it generates with uncertain dataState
Unfortunately, we cannot enforce this expectation for all users 😕 I'd say the stopgap is the incremental codemod approach mentioned above.
Hi @EvgeniyKumachev ,
I appreciate the time you put into trying to fix this. 🙂
I don't think my team would be glad to review few thousand lines of changes with migration from codegen to client preset 😅.
I totally understand the concern, I highly recommend doing this in multiple PRs. You can target where you'd like to run the migrations by changing a
generatesblocks' key. Hopefully, there are tests in place to catch potential errors.In my case bundle size and strictness of dataState property isn't really an issue. Wouldn't want to stockpile techdebt either so would be nice to have some stopgap option where it generates with uncertain dataState
Unfortunately, we cannot enforce this expectation for all users 😕 I'd say the stopgap is the incremental codemod approach mentioned above.
Got it, thanks. I'll make a separate fork then with support for v4 as described above. Sadly i can't justify to my PM refactor of the whole codebase to support strict typing of one property that we don't even use
Hey all! If your repo is using typescript and react, I've found that it's unnecessary to run the migration script from @eddeee888.
His script will copy your GraphQL queries into you components. However, you can access those from the gql/graphql.ts file generated by the client-preset. This is a pretty good guide to understand client-preset.
Here's what I did instead.
// codegen.ts
// I kept both generated files so I could incrementally migrate my files. When I'm ready to migrate to v4, I'll remove the original.
const config: CodegenConfig = {
...
ignoreNoDocuments: true,
generates: {
["./app/generated.ts"]: { // This is my original file that is generating the QueryHooks
plugins: [
"typescript",
"typescript-operations",
"typescript-react-apollo",
"named-operations-object",
],
},
"./app/gql/": { // These is my new generated files.
preset: "client",
presetConfig: {
fragmentMasking: false,
},
},
},
...
}
// SupportBanner.tsx
import {
BannerPreferencesDocument,
BannerPreferencesQuery,
DismissSupportBannerDocument,
DismissSupportBannerMutation,
SupportPreferenceCategoryEnum,
} from "gql/graphql";
const SupportBanner: React.FC<SupportBannerProps> = ({
topic,
...viewProps
}): React.ReactElement | null => {
const { data, error, loading } = useQuery<BannerPreferencesQuery>(
BannerPreferencesDocument,
);
const [dismissBanner] = useMutation<DismissSupportBannerMutation>(
DismissSupportBannerDocument,
);
Oh no! Seems like our huge codebase is now stuck at Apollo v3 :( The static hook generation is a massively integral part of our app. We have also developed a schema versioning system with static assurence above it, to ensure our 30+ projects always run with compatible API. All our GQL documents are in own .gql files. This sped up intellisense speed a lot, not having to parse gql from ts files. We have a huge schema. It was a big migration, I am definitely not going back. What would it take for codegen to support Apollo v4? We might consider forking it.
Hi @vitexikora , I'm curious about the performance issue you mention:
This sped up intellisense speed a lot, not having to parse gql from ts files
May I know if you were using Client Preset when you see this issue? If so, could you please elaborate which part of intellisense speed was impacted? The document node type? Codegen run duration ? Or something else?
What would it take for codegen to support Apollo v4? We might consider forking it.
I can't think of a way to preserve Apollo Client's type overloads using the generated hooks approach.
Note that dataState type overload is one example introduced in v4, and there could be more that I'm not aware of, and may increase in the future
i.e. the effort to maintain type correctness for generated hooks would increase exponentially
How software developers just LOVE to break backward compatibility (looking at you, React Router). Like some of the people who commented above, we also have quite a large codebase with generated hooks. So thanks to the Apollo developers - since we’ll have to rewrite and refactor everything anyway, we can now take the opportunity to consider some alternatives lol
May I know if you were using Client Preset when you see this issue? If so, could you please elaborate which part of intellisense speed was impacted? The document node type? Codegen run duration ? Or something else?
@eddeee888 The whole intellisense experience was impacted. Even waiting for a component name to import or using ctrl+click. It was somehow being blocked centrally by GQL type parsing of some sort. I have spent several hours on several occasions trying to get around this, because it was a big burden. However, it was difficult to reliable simulate. But specifying in .graphqlrc to walk through only *.gql files worked like a magic trick. So I rewrote the rest of gql segments to own files.
the effort to maintain type correctness for generated hooks would increase exponentially
So the only issue is higher maintenance. I mean if it meant to have some any types here and there but the main thing would work, I would really gladly take it. This principle of using generated hooks is really good, it has many upsides. I don't want to 'downgrade' our app.
we can now take the opportunity to consider some alternatives lol
@e965 I feel you. We just survived going RR5 -> RR7, took a week, lot of swearing, and we lost some blood (notably regex paths). But I refused to migrate ESLint, there was always this level of hate we shared against this slow plugin-overloaded thing, so when they announced the big BC break, we switched to Biome. I was missing some stuff for a while, but I am never looking back :)
Thanks for the details @vitexikora !
The whole intellisense experience was impacted. Even waiting for a component name to import or using ctrl+click. It was somehow being blocked centrally by GQL type parsing of some sort. I have spent several hours on several occasions trying to get around this, because it was a big burden. However, it was difficult to reliable simulate. But specifying in .graphqlrc to walk through only *.gql files worked like a magic trick. So I rewrote the rest of gql segments to own files.
If I understood this correctly, you were using some like this for document path: documents: 'src/**/*.ts' which was slow. Then you switched to documents: 'src/**/*.gql' and it was much faster.
If that was the case, most likely Intellisense was reading too many .ts files. I suspect you'd have the same performance if you narrowed down your file pattens to something like documents: 'src/**/*.graphql.ts' with .graphql.ts files have GraphQL documents and follow either co-located or near-opeartion-file patterns.
This principle of using generated hooks is really good, it has many upsides. I don't want to 'downgrade' our app.
Could you please help me understand what you like about generated hooks? 🙂
I suspect you'd have the same performance if you narrowed down your file pattens (...)
I thought that too, but when I was testing this some time ago, it still had some issues. Then I tried using only *.gql and it was so much better that I have instantly followed that path. The client-preset is not compatible with gql files?
Could you please help me understand what you like about generated hooks? 🙂
Well now that I have got time to dig deeper in it, the functions that we had built above generated hooks may possibly work with client-preset as well. I may have been misled by some posts hating on the client-preset.
I thought that too, but when I was testing this some time ago, it still had some issues. Then I tried using only *.gql and it was so much better that I have instantly followed that path. The client-preset is not compatible with gql files?
Client Preset recommends operation and fragment co-location which means these should live in the same file as your component.
.graphql/.gql files works, but the DX is a bit awkward because that's not approach it is built for.
Client Preset recommends operation and fragment co-location which means these should live in the same file as your component.
What about a scenario, when there is a modular system that defines some core GQL types, like File, Image, PublicUser, etc. And then there are custom modules that use them, eg. news { id, author { ...PublicUser } }. Our modules are decoupled from the core, they only share some core types. So an operation lives in something like src/modules/News/gql/documents/*.gql, News related fragments are nearby, but core types are in src/core/graphql/fragments/*.gql. Is this feasible with Client Preset?
.graphql/.gqlfiles works, but the DX is a bit awkward because that's not approach it is built for.
In what ways? Is there a comparison of both?
What about a scenario, when there is a modular system that defines some core GQL types, like File, Image, PublicUser, etc. And then there are custom modules that use them, eg. news { id, author { ...PublicUser } }. Our modules are decoupled from the core, they only share some core types. So an operation lives in something like src/modules/News/gql/documents/.gql, News related fragments are nearby, but core types are in src/core/graphql/fragments/.gql. Is this feasible with Client Preset?
Yes, you could split GraphQL docs from components if required. Here's an example:
// src/modules/News/gql/documents/News.gql.ts
import { graphql } from 'src/gql';
export const NewsDoc = graphql(`
query news {
id
author {
...PublicUser
}
}
`)
// src/core/graphql/fragments/PublicUser.gql.ts
import { graphql } from 'src/gql';
export const PublicUserFragmentDoc = graphql(`
query news {
fragment PublicUser on User {
id
}
}
`);
// src/YourComponent.tsx
import { useQuery } from '@apollo/client/react';
import { NewsDoc } from from 'src/modules/News/gql/documents/News.gql';
export const YourComponent = () => {
useQuery(NewsDoc);
}
In what ways? Is there a comparison of both?
Client Preset uses graphql(...) function as GraphQL doc markers (same as .gql ) AND to get the document doc (wired up in hooks, or manually imported from generated files), . So if you updated the content of .gql files, you'd have to update graphql(...) content as well
I kind of like the clarity and tidiness of pure .gql files, after I migrated all projects to use them exclusively.
So I would have to run some kind of a command after updating them? (Just like I do now with generated hooks?).
Thank you for your explanations, I am sure it will help many developers considering the migration.
So I would have to run some kind of a command after updating them? (Just like I do now with generated hooks?).
That's right, you'd run codegen to generate types for your documents. However, instead of importing generated hooks, you'd use the document result in your TS file directly.
Here's the Client Preset guide