GraphQL Code Generator and Webpack integration #3494
-
We were thinking a lot about the integration of GraphQL Codegen and Webpack. There was a few attempts by our team and the community to integrate the 2 libraries, this issue will cover some of those and explain the possible integrations of GraphQL Codegen and Webpack. We had an initial implementation for this integration (also, #802), implemented by @kamilkisiela , but it was buggy, mostly because we couldn't integrate the watchers of the two into a single working process. It was also a generic integration and not a specific loader so configuring it wasn't so simple. It seems like the best way to integrate GraphQL Codegen and Webpack is by implementing a custom Webpack loader, that builds JS code for a specific purpose, and uses Codegen to enrich the built code with typings or anything else you can't get in runtime. The main question is where the codegen plugin that supports the Webpack loader should be located - and we think it should be in the same repo as the loader, because the two are related and should work together. We had a few attempts to think together and try to do those:
Currently, there are a few libraries that already does that for you:
So as you see - there could be many types of integrations with Webpack, and the goal of this issue is to discuss that. If you are implementing a Webpack loader and would like to generate something with the codegen, feel free to contact us and we will happily help you with that. I would like to thank to everyone from the community who helped on that topic (@mxstbr @eddeee888 @CarloPalinckx @slikts @dobesv and more) ❤️ |
Beta Was this translation helpful? Give feedback.
Replies: 14 comments
-
Perhaps we could adjust this issue a bit to include the actual developer experience benefits that we try to achieve? I would like to shift the discussion from "how can be build X" to "How can we improve experience X" and work our way to the implementation from there since there are a lot of possible alternatives to the webpack approach that achieve the same (like a babel plugin or macro, rollup is also something to consider) 🙂. The ideas I had when starting my PR were the following: Imo, the best achievable developer experience we can get when working with graphql files (or gql ttl's) is something like: import useGetFooQuery from 'getFoo.graphql';
// or
const useGetFooQuery = gql`
query getFoo {
foo
}
`
...
// data would be fully typed here
const { data, loading, error } = useGetFooQuery() It would also be great if we could work more towards easier code-splitting. Since all generated implementations are all stacked in a single file now. They are pretty hard to code-split. If the implementations only exist at build time and not in the fs, they would automatically provide better support for this. |
Beta Was this translation helpful? Give feedback.
-
@CarloPalinckx we actually have achieved this exact behavior in our (private) project, and it's awesome. And it wasn't that hard. The core issue, IMO, is that TypeScript/Flow don't really interact well with outside build tools: whatever code gets generated, say, in a Webpack loader or Babel plugin/macro, is not recognized by TS and I don't think it will be in the foreseeable future. So you are going to need to separately generate const docReplacer = (k, v) => (k === 'loc' ? undefined : v);
module.exports = {
plugin: (schema, documents, config, info) => {
return documents
.map(doc => {
if (config.externalFragments) {
doc.content.definitions.push(...config.externalFragments.map(ext => ext.node));
}
const firstDef = doc.content.definitions[0];
const { kind } = firstDef;
const name = firstDef.name ? firstDef.name.value : 'Unnamed_1_';
const docString = JSON.stringify(
doc.content,
docReplacer,
2
);
if (kind === 'OperationDefinition' && firstDef.operation === 'query') {
const hasVars = firstDef.variableDefinitions.length > 0;
return `<YOUR_CODE_HERE>`;
} else if (kind === 'OperationDefinition' && firstDef.operation === 'mutation') {
const hasVars = firstDef.variableDefinitions.length > 0;
return `<YOUR_CODE_HERE>`;
}
// otherwise just emit types
return '';
})
.join('\n');
},
}; ... where |
Beta Was this translation helpful? Give feedback.
-
This is actually what I did in my PR #2732 , it just generates a single file full of: declare module "YOUR_GQL_FILE" {
// your typed operation based on your .graphql file
}
I would argue that a single file with all declared modules is a bit better than all the separate files from a maintenance perspective.
I think writing the implementation to a file is exactly what we are trying to avoid because of the aforementioned reasons 🙂 |
Beta Was this translation helpful? Give feedback.
-
The webpack loader idea sounds nice, but consider the drawbacks:
In our case currently we do use a webpack loader ( |
Beta Was this translation helpful? Give feedback.
-
@dobesv is it safe to assume that people doing SSR will at least have babel running? Since it transforms jsx et.all? That would make a babel plugin a more suitable candidate than a webpack loader. |
Beta Was this translation helpful? Give feedback.
-
@CarloPalinckx I previously thought of a babel plugin as well, and that would be nice, but ATM you cannot write babel plugins that parse languages other than JavaScript. Babel is a tool for translating between versions of JavaScript/ECMAScript/typescript, not a general purpose "anything to js" tool. That is how I ended up building graphql2js. I think it would be nice if babel did support this kind of use case better, just to avoid running two tools. But in the end, babel would just be there to hand filenames to your plugin, so I can see the argument to just say people should write their own separate tool for that. |
Beta Was this translation helpful? Give feedback.
-
@dobesv Turns out you actually can 🙂 there is a plugin that does something similar https://github.com/airbnb/babel-plugin-inline-react-svg |
Beta Was this translation helpful? Give feedback.
-
What these plugins actually do is the copy the target of the import into the file that imports it. They do not translate the imported file to a JavaScript file. There's already a babel plugin that does this for graphql files, it operates similar to the graphql-tag/loader. I used it for a little while but it has a major, breaking drawback: it doesn't work well with caches. Babel caches, webpack caches, and hot reloading. These systems are not aware of the dependency of the file doing the import on the file being imported. So if you modify the graphql file, the change doesn't show up in the application when you are using hot reloads and things like that. Note also that a babel plugin will not help you if you are using typescript, since typescript does not run babel plugins, and thus the type information has to come from another source. |
Beta Was this translation helpful? Give feedback.
-
What would be the difference between the server-side and client-side version of the files? Last time I used NextJS with Apollo, I could use |
Beta Was this translation helpful? Give feedback.
-
I think the generated files would be the same between client and server, at least for my project they are the same (using graphql-tag/loader) |
Beta Was this translation helpful? Give feedback.
-
So I've been using However when working on a talk about the whole process I stumbled upon It would be awesome to have something like it as a core feature 🚀 |
Beta Was this translation helpful? Give feedback.
-
I agree with @lukasluecke, |
Beta Was this translation helpful? Give feedback.
-
what's the status of this? graphql-let doesn't seem to be maintained anymore |
Beta Was this translation helpful? Give feedback.
So I've been using
graphql-code-generator
for a few projects since over a year now, and was pretty content with my current configuration and usage.However when working on a talk about the whole process I stumbled upon
graphql-let
(as mentioned in the issue description, it's a webpack-loader built aroundgraphql-code-generator
) and have to say that it makes so much sense to have this kind of integration!It would be awesome to have something like it as a core feature 🚀