-
Notifications
You must be signed in to change notification settings - Fork 466
Description
Hello Apollo team and Apollo Codegen users!
I’m Dotan and I’ve created a library called graphql-code-generator, which we maintain for the past 4 years that aims to solve some of the same issues as Apollo Codegen.
While working with a very large codebase of one of our new clients that was using Apollo Codegen, we’ve seen that the support for Apollo Codegen and it’s issues has slowed down in recent years, (looking at the history of the apollo-codegen-core, apollo-codegen-flow and the apollo-codegen-typescript libraries, it looks like the last meaningful workload was made around August 2018) and we wanted to offer our help!
On the following issue I will try to describe everything I can about the 2 codegens, in order to collaborate together on two possible paths:
- Integrate graphql-code-generator into Apollo Codegen while keeping Apollo Codegen as a wrapper (we don’t care about credit :) )
- An easy path for Apollo Codegen users to migrate to simply using graphql-code-generator
At the end, the goal here is to share all the work we’ve done in graphql-code-generator with Apollo and it’s community, in order to help Apollo in whatever way Apollo would see fit.
I will write about the difference between the codegens as they are today, two possible migration paths and a list of all open issues on Apollo Codegen that could be addressed by this collaboration.
We would love any feedback, from the great people at Apollo and it’s amazing community members!
We are currently just talking about the Typescript and Flow generators, as @designatednerd has been doing amazing work on the Swift codegen so no need for our help there!
(But we are still happy to help and collaborate there if needed, as we have Swift codegen plugins maintained by our community).
Ok here we go:
We’ve reviewed the implementation of Apollo Codegen and reviewed all open issues related to codegen on this repo and came up with a possible plan to move forward we would love your feedback on.
There are some differences in the generated output of the two generators.
We’ll explain here the differences in philosophies but we’ve also created a configuration for graphql-code-generator that generates similar output to Apollo Codegen and supports all the flag options of the Apollo CLI in order to make the switch easy and for you to have the option to gradually adopt our best practices or to simply stick with your own.
It is also a good time to kick start this collaborative initiative as we’ve started planning our next major version, GraphQL Code Generator 2.0, and would be great to get Apollo and the Apollo community be an active part in shaping that next release!
(Including the new TypedDocumentNode plugin)
First, what is GraphQL Codegen and what are the Differences compared to Apollo Codegen
Here we try to describe the current differences between the tools, even though they can be almost identical with the right configuration in GraphQL Code Generator
- Output
- Apollo codegen yields multiple files - a file per input file, and GraphQL Codegen prefers a single file to be generated
- Multiple files are harder to maintain and are spread through the entire codebase, and generated output should always take into account the imports (which are not always predictable, especially in large/complex codebases, or monorepos).
- Performance - We’ve seen users ask for multiple files because they thought it would give them better build performance. In every single case, we’ve demonstrated to them that a single file with the right configurations is much faster
It’s still possible to generate multiple files (with a codegenpreset
) if you wish to.
- Intermediate types
- GraphQL Codegen dropped default support for generating types of nested selection sets. We prefer to let developers have the ability to do that with GraphQL fragments.
TypeScript (and Flow) allow you to access nested types easily and alias it if you wish to. - GraphQL Codegen still supports generating intermediate types, but as a general approach, we recommend you not to do that (harder to maintain, causes mismatch issues, doesn’t scale with flexible configurations)
- GraphQL Codegen dropped default support for generating types of nested selection sets. We prefer to let developers have the ability to do that with GraphQL fragments.
- Yaml config
- One of the goals of GraphQL Codegen is to be flexible as possible. That means, you can easily customize the output to match your needs, and especially makes it simpler to integrate with existing codebases.
- You can also use it programmatically, and have low-level over the input and output. You can also configure it with JSON or JavaScript if you don’t like YAML.
- Loaders
- Codegen automatically loads your schema from any source (file, local file, remote file, code files, GitHub, Apollo Graph Manager and more).
- GraphQL Codegen also knows how to extract and parse your GraphQL operations from your components code.
- You can easily customize the way you load your schema and your operations.
- Plugins
- Our community has over 100 plugins, for various languages, platforms and frameworks.
- Creating your own plugin is as simple as creating a local file in your repository. Codegen will pick it and integrate your custom plugin with other plugins.
- More than just basic types
- Resolvers signature - codegen allows you to generate type-safe resolvers signature (for TS, Flow and Java). You can also bring your own models types for types and context, and integrate it within the generated types. It will fully type your resolvers’ parent value, arguments, context and return value.
- Ready-to-use code - ensures better usage in large teams. You can generate fully-typed React-Apollo Hooks, Apollo-Angular Services for wrapping data fetching, Vue-Apollo data components and more.
- Fragment matcher - GraphQL Codegen can generate the fragments-matcher or
possibleTypes
object required by Apollo-Client. - Precompiled
DocumentNode
- codegen can pre-compile GraphQL operations into DocumentNode and eliminate the need forgraphql-tag
. - Introspection or schema-ast - Codegen has some abstractions for GraphQL and GraphQL-Tools, in order to make it simpler for your to create GraphQL introspection file, or even for merging schemas and printing it as GraphQL SDL.
- Activity on the repo
- We encourage the community to share all ideas, concepts and plugins they need. We aim to allow codegen to be flexible enough and simplify it’s integration in all codebases.
- We aim to release a new version with dependencies updates and bug fixes every week (and each change we do get it’s own alpha release automatically, so no need to wait for the new release in order to test new features or bug fixes). This has been that case for about 4 years and we don’t have any intention of stopping.
- Integration with other tools
- Federation - you can integrate your Apollo Federation backend and generate resolvers signatures based on the capabilities of each service.
- More integrations - TypeGraphQL, Apollo Local State, Gatsby (and more).
Possible Migration Guide
As an Apollo Codegen user that wishes to migrate to GraphQL-Codegen, you have 2 options - either to use GraphQL Codegen and have a very similar output (with zero to minimal code changes), or adjust your project to the concepts of GraphQL Codegen. You should choose according to the size of your codebase and it’s complexity.
Option 1: Migrate to GraphQL-Codegen concepts
One of the major differences is the output itself - codegen aims to generate a single file with all the types. It’s easier for the IDE and for the TypeScript compiler.
The equivalent for TypeScript types based on GraphQL schema and operations, is the following configuration:
schema: PATH_OR_URL_TO_SCHEMA
documents: GLOB_EXPRESSION_FOR_OPERATIONS
generates:
./src/types.ts:
plugins:
- typescript
- typescript-operations
You can start with it, and gradually extend it with more plugins and more features, according to your needs.
Option 2: Have the same output
This solution will leverage more complex codegen features and configurations, in order to create output that will be compatible with the same file-names and identifiers names that Apollo-Codegen creates today.
The following configuration file will help you:
# The value you are using today for `client:download-schema` - no need to download, store and then
# use it - codegen does that automatically.
schema:
- PATH_OR_URL_TO_SCHEMA
documents:
- GLOB_EXPRESSION_FOR_OPERATIONS # Equivalent for `--includes` flag, you can also use negative glob to `--excludes`
config: # The following configuration will adjust output to the defaults of Apollo-Codegen, and should be compatible with most use-cases.
preResolveTypes: true # Simplifies the generated types
namingConvention: keep # Keeps naming as-is
avoidOptionals: # Avoids optionals on the level of the field
field: true
nonOptionalTypename: true # Forces `__typename` on all selection sets
skipTypeNameForRoot: true # Don't generate __typename for root types
generates:
./src/globalTypes.ts: # Equivalent for `--globalTypesFile` flag
plugins:
- typescript # Generates based types based on your schema
./src/: # Points to your project root directory.
preset: near-operation-file # Tells codegen to generate multiple files instead of one
presetConfig: {
extension: ".graphql.interface.ts" # Matches the existing Apollo-Codegen file-naming
baseTypesPath: "./globalTypes.ts" # Points to the base types file
plugins:
- typescript-operations # Generates types based on your operations
This should create output that is very similar and compatible with the existing Apollo-Codegen implementation, and should make it simpler for you to migrate.
Also, most of the configuration flags of Apollo-Codegen are supported in codegen, so if you are using custom setup, you should be able to use the base file above.
If your codebase is using intermediate types, you can add typescript-compatibility
plugin to get those generated for you.
List of issues / PRs and their state compared to GraphQL-Codegen
Issues
- Add an option to remove underscores from generated interfaces. #1414 - graphql-codegen have it by default, and it’s customizable (
namingConvention
) - Add an option to remove underscores from generated interfaces. #1414 (comment) - Maybe we can create a plugin for intermediate types, just for annoying people like that. I think our recent blog post about codegen will help
- Apollo Codegen neither should ignore errors in gql queries nor skip bad queries #979 - We have it built-in in our loaders, you can pass assumeValidSDL and it will skip validation. Maybe it should be better documented.
- Typescript generation does not accurately reflect interface types #1568 - In our codegen it will generate it correctly, and you’ll be able to use
__typename
to identify the type correctly ;) - Generate undefined instead of null in typescript #622 -
maybeValue
in config - Codegen does not work with fragments from external package #1388 - Works in codegen, we can load fragment from packages (either code or exports)
- Syntax error in .graphql file not reported directly #1436 - Works in our codegen, throws error.
- Codegen should take @include and @skip into account #888 - Not supported yet.
- Codegen won't show Warning or Error for conflicting Fragments #1803 - Codegen will throw an error (valid behaviour)
- Add option to enable new flow inexact object syntax #1800 - Available in codegen (
useExactValue
config) - codegen - typescript idea of improvement - generate the operation too #1767 - Available in codegen.
- Generating flowtypes with a graphql input puts generated type in every generated file #988 - Available in codegen.
- apollo client:codegen Add custom header to output #1043 - Available in codegen.
- apollo client:codegen Option for different TypeScript enum output #1044 - Available in codegen.
- apollo server typescript codegen #1735 - Available in codegen.
- apollo client:codegen --namespace is not working for TypeScript #1204 - We prefer not to use namespaces in TS. But we do have an example for wrapping all types with an interface (using add plugin).
- Can use @cacheControl directive with codegen #791 - In codegen it’s possible to add it manually to the codegen schema, so it will work.
- Generated Flow type files should have
// @flow strict
pragma #1576 - Available in codegen. - Error output of
codegen:generate
is absurdly verbose #1297 - Codegen status update is based on listr, and not verbose when not needed. - Codegen does not automatically append "Query" or "Mutation" to the generated type #1366 - Available in codegen.
- Codegen: Also generate a definition file for .graphql fils #604 - Available in codegen.
- Feature Request: Generate an
index.ts
file in codegen for typescript #1161 - Not needed in codegen. - codegen:generate Global typings error in Angular workspace #1310 - Not needed in codegen.
- [Feature Request] Allow option to remove/omit fragment prefix naming for better re-use #1207 - Not needed in codegen.
- Bug: an input field which defines a default value results in a required field in the generated typedefs #1036 - Available in codegen.
- Codegen in --watch mode doesn't handle deletions/renames fully #873 - Available in codegen, because we load everything from scratch every execution.
- Apollo client:codegen doesn't work with dynamic interpolated fragment name #1375 - Works in codegen.
- Codegen from Apollo
executableSchema
object #1046 - Available in codegen. - Apollo codegen query results should not be optional for pure @client queries #932 - Works in codegen.
- Feature Request: Add ability to validate types are correct #833 - Codegen does that by default.
- Code generation templates support #877 - Available in codegen.
- [Flow] Export each possible variant of a union as a separate type #1223 - You can distinguish types with __typename easily.
- Enums should not be sorted alphabetically by default #1760 - Codegen doesn’t change the sort, it will be used as-is, using graphql-js library.
- Codegen in --watch mode crashes on fatal errors #1920 - Codegen doesn’t stop during watch mode, it just waits for changes and re-runs.
- Yarn V2 compatibility #1850 - Codegen works with Yarn2 PnP.
- [apollo-codegen-typescript] interfaces should perhaps be alias types #2016 - Codegen allows you to customize the output and choose between interface or type.
- Provide a default export for typescript codegen files to work nicely with webpack
--isolatedModules
flag #2030 - Codegen doesn’t produce empty files. - Apollo Client Codegen does not take into account Field Policies (Apollo Client 3.0) #2044 - Field policies can be represented as a GraphQL schema, and then loaded directly into Codegen just like any other schemas.
PRs
- Generate optional TypeScript fields when @include or @skip directive is used #1854 - Supported it codegen.
- Add option to keep schema enum order for code generating #2043 - It shouldn’t change it in codegen.
- feat: add tsUseOptionalForNullables option to ts codegen #1766 - Configurable with
avoidOptionals
config. - Emit TypeScript string unions instead of enums by default #1750 - Available using
enumsAsTypes
- Document codegen requirement about SDL file extension #1504 - Codegen supports loading schema from local files, url and more.
Configuration mapping
The following is a reference for configuration mapping between Apollo-Codegen and GraphQL-Codegen, you might find it helpful it you are in the process of migrating it:
graph
/variant
/key
=> You can use apollo-graph loader for that.addTypename
=>addTypename
customScalarsPrefix
/passthroughCustomScalars
=>scalars
outputFlat
=> Not supported, but could be added as a preset.globalTypesFile
=> simply change the output name in codegen config.excludes
=> negativeglob
indocuments
sectionendpoint
/header
/localSchemaFile
=>schema
item, with url loader.includes
=>documents
mergeInFieldsFromFragmentSpreads
=>flattenGeneratedTypes
flag.namespace
=> wrapper withadd
plugintagName
=>pluckConfig
target
=> list of plugins - this will support only flow / typescripttsFileExtension
=> will update the output extension, but also we need to make sure to change some configurations related to enums (for exampleenumAsType: true
) if the extension if.d.ts
.useFlowExactObjects
=> flow only,useFlowExactObjects
useFlowReadOnlyTypes
=> flow only,useFlowReadOnlyTypes
useReadOnlyTypes
=>immutableTypes
configuration flag