Skip to content
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

Codegen collaboration with Apollo Codegen and GraphQL Code Generator #2053

Open
dotansimha opened this issue Jul 27, 2020 · 30 comments
Open

Comments

@dotansimha
Copy link

dotansimha commented Jul 27, 2020

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:

  1. Integrate graphql-code-generator into Apollo Codegen while keeping Apollo Codegen as a wrapper (we don’t care about credit :) )
  2. 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 codegen preset) 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)
  • 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 for graphql-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

PRs

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 => negative glob in documents section
  • endpoint / header / localSchemaFile => schema item, with url loader.
  • includes => documents
  • mergeInFieldsFromFragmentSpreads => flattenGeneratedTypes flag.
  • namespace => wrapper with add plugin
  • tagName => pluckConfig
  • target => list of plugins - this will support only flow / typescript
  • tsFileExtension => will update the output extension, but also we need to make sure to change some configurations related to enums (for example enumAsType: true) if the extension if .d.ts.
  • useFlowExactObjects => flow only, useFlowExactObjects
  • useFlowReadOnlyTypes => flow only, useFlowReadOnlyTypes
  • useReadOnlyTypes => immutableTypes configuration flag
@kaleb
Copy link

kaleb commented Sep 3, 2020

Thank you for this. I was interested in using hook generation and it looks like this is a good guide for me to migrate.

@kaleb
Copy link

kaleb commented Sep 3, 2020

@dotansimha one thing left out is the apollo option of --passthroughCustomScalars. What I did was add the scalars key to my codegen.yml file:

config:
  scalars:
    DateTime: string
    Float: number
    ...

@jimisaacs
Copy link

What is this?

      extension: ".graphql.interface.ts" # Matches the existing Apollo-Codegen file-naming

We've been using apollo for years now, and I've never seen this naming convention used in output.

@jimisaacs
Copy link

jimisaacs commented Jan 26, 2021

Does the graphql codegen type names match apollo's?
e.g.

OperationOrFragmentName_fieldName_fieldName_fieldName

Located in file: flatPathOrFolderPath/OperationOrFragmentName.ts

I ask, because a significant deviation from this would hurt quite a bit.

@jimisaacs
Copy link

I've assessed transitioning, and TBH, I find Apollo's generated types/interfaces much simpler to reason about. Though I won't lie that I'm coming with a bias, based on years of use, and thousands of usages in our codebase.

@dotansimha
Copy link
Author

Does the graphql codegen type names match apollo's?
e.g.

OperationOrFragmentName_fieldName_fieldName_fieldName

Located in file: flatPathOrFolderPath/OperationOrFragmentName.ts

I ask, because a significant deviation from this would hurt quite a bit.

GraphQL Codegen doesn't generate intermediate types by default. There are solution, but in most cases just using fragments makes it simpler (and more correct in terms of GraphQL workflow).

What is this?

      extension: ".graphql.interface.ts" # Matches the existing Apollo-Codegen file-naming

We've been using apollo for years now, and I've never seen this naming convention used in output.

You are right, maybe it was outdated :) But the point is that you can customize it.

I've assessed transitioning, and TBH, I find Apollo's generated types/interfaces much simpler to reason about. Though I won't lie that I'm coming with a bias, based on years of use, and thousands of usages in our codebase.

Sure. I don't think there is a right/wrong choice here. It all depends on your workflow and what works best for you!

@xxleyi
Copy link

xxleyi commented Jun 21, 2021

@dotansimha Does outputFlat option have some support?

@dotansimha
Copy link
Author

outputFlat

With codegen, we recommend generated all types into a single file. When you want to generate more than one file (or, separate files based on operations/types), you can use presets feature. We currently have near-operation-file preset that has output similar to the default of apollo-tooling, but we don't have a preset that outputs something similar to outputFlat (should be simple to create, btw).

@dylanwulf
Copy link

dylanwulf commented Jun 25, 2021

strongly considering switching over to GraphQL Code Generator, mainly due to #2232 and #1750 (that last one has been open for 1.5 years now with no word from maintainers!). The one thing GraphQL Code Generator can't do is take my schema URL from my .env file

@dotansimha
Copy link
Author

dotansimha commented Jun 27, 2021

strongly considering switching over to GraphQL Code Generator, mainly due to #2232 and #1750 (that last one has been open for 1.5 years now with no word from maintainers!). The one thing GraphQL Code Generator can't do is take my schema URL from my .env file

see https://www.graphql-code-generator.com/docs/getting-started/codegen-config#environment-variables and https://www.graphql-code-generator.com/docs/getting-started/require-field#dotenv-integration

@dylanwulf
Copy link

@dotansimha Oh! totally missed that part, thank you!

@nathanredblur
Copy link

sorry, is not clear for me, how to generate
fragment-matcher and typeDef configurations used in apollo client.

this is my long road (and difficulties) to generate this types:

  1. add a fixed version in my package json for my mono-repo (that I should remove because in the mono repo there are modules that require older versions)
"resolutions": {
    "graphql": "^15.5.3"
  }
  1. move all my schemas used in my local resolvers out of his file
    from this
// AuthUser.js
export const schema = gql`
  extend type Auth__User {
    canUseSomething: Boolean
  }
`;

export const resolvers = {
...

to a file that needs to be in the root folder (because in other location generate issues.) with all local schemas of all my resolvers

// local-schema.graphql
extend type Auth__User {
  canUseSomething: Boolean
}
...
  1. rewrite my queries used in writeQuery
    from this
const WRITE_NETWORK_QUERY = gql`
  query WriteNetwork {
    networkStatus 
  }
`;
...
      cache.writeQuery({
          query: WRITE_NETWORK_QUERY,
          data: {
            networkStatus: {
              __typename: 'Local__NetworkStatus',
              online: true,
              retrying: true,
            },
          },
        });

to this

const WRITE_NETWORK_QUERY = gql`
  query WriteNetwork {
    networkStatus {
      retrying
      online
    }
  }
`;
....
  1. run this long commands
"apollo:download": "apollo service:download --endpoint=https://app.stg.airtm.io/graphql src/lib/apollo/graphql.schema.json",

"apollo:generate": "apollo codegen:generate --localSchemaFile=src/lib/apollo/graphql.schema.json,local-schema.graphql --target=typescript --tagName=gql",

and I don't know

  1. how to move local-schema.graphql and splited so each type can live in the folder where is defined his resolver
  2. how to generate fragment-matche used in possibleTypes in InMemoryCache
  3. how to get typeDefs used by new ApolloClient

@franleplant
Copy link

franleplant commented Mar 2, 2022

One other thing not mentioned here is that apollo codegen can get really slow and I know that graphql-codegen scales really well.

Would be awesome to automate the migration, for larger codebases.

@poppywu
Copy link

poppywu commented Jun 30, 2022

Hi, thank you for this migration file, it's super helpful!
For the customScalarsPrefix / passthroughCustomScalars => scalars, currently I have --customScalarsPrefix=test$, how should I config scalars to obtain the same prefix? Are there any example? Thanks!!

@antonvialibri1
Copy link

I tried migrating from Apollo Codegen to GraphQL Codegen, but it didn't work.

We generate GraphQL types through Apollo Codegen using the following command and apollo.config.js file:

apollo client:codegen --target typescript --outputFlat
module.exports = {
  client: {
    service: {
      name: 'project',
      localSchemaFile: 'storage/app/lighthouse-schema.graphql'
    },
    includes: [
      'assets/shared/**/*.js',
      'assets/project1/src/**/*.js',
      'assets/project2/js/**/*.js',
      'assets/project3/src/**/*.js'
    ]
  }
};

It's a monorepo project which uses Yarn workspaces. The GraphQL schema is generated by Lighthouse (lighthouse-schema.graphql).

apollo client:codegen --target typescript --outputFlat scans all the JS source code in all projects (mainly React components that use @apollo/client's gql, useQuery and useMutation) and generates multiple .ts type files within the __generated__ folder.

This is our baseline and it works well. We decided to give GraphQL Codegen a try as apollo CLI codegen seems to be deprecated and still depends on the graphql package at version 15 (the current major version as of today is 16).

Therefore, I've installed GraphQL Codegen and upgraded the graphql package to major version 16:

// package.json
...
"dependencies": {
    "graphql": "^16.5.0",
    ...
},
"devDependencies": {
    "@graphql-codegen/typescript": "2.7.2",
    "@graphql-codegen/typescript-compatibility": "2.1.5",
    "@graphql-codegen/typescript-operations": "2.5.2",
    "@graphql-codegen/typescript-react-apollo": "3.3.2",
    "@graphql-codegen/typescript-document-nodes": "2.3.2",
    "@graphql-codegen/fragment-matcher": "3.3.0",
    "@graphql-codegen/typescript-graphql-files-modules": "2.2.0",
    "@graphql-codegen/introspection": "2.2.0",
    "@graphql-codegen/cli": "2.9.1",
    "@graphql-codegen/near-operation-file-preset": "2.4.0"
}
...

Then I've created the following config graphql-codegen.yml file by following @dotansimha's migration advice in this issue (#2053 (comment)):

schema: "./storage/app/lighthouse-schema.graphql"
documents:
  - assets/shared/src/**/*.js
  - assets/project1/src/**/*.js
  - assets/project2/src/**/*.js
  - assets/project3/src/**/*.js
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:
  ./assets/shared/__generated__/globalTypes.ts: # Equivalent for `--globalTypesFile` flag
    plugins:
      - typescript # Generates based types based on your schema
      - typescript-compatibility
  ./assets/shared/__generated__:
    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
      - typescript-compatibility

Now, when I run yarn graphql-codegen I get the following errors:

% yarn graphql-codegen        
yarn run v1.22.19
$ graphql-codegen --config graphql-codegen.yml
✔ Parse Configuration
⚠ Generate outputs
  ❯ Generate ./assets/shared/__generated__/globalTypes.ts
    ✔ Load GraphQL schemas
    ✔ Load GraphQL documents
    ✖ GraphQL Document Validation failed with 2 errors;
      Error 0: GraphQLDocumentError: Unknown fragment "UserContextFragment".
      at /Users/me/repositories/project/assets/project2/src/App.js:4:10
      Error 1: GraphQLDocumentError: Unknown fragment "UserContextFragment".
      at /Users/me/repositories/project/assets/project3/src/App.js:4:10
  ❯ Generate to ./assets/shared/__generated__ (using EXPERIMENTAL preset "near-operation-file")
    ✔ Load GraphQL schemas
    ✔ Load GraphQL documents
    ✖ visitFn.call is not a function
✨  Done in 3.29s.

UserContextFragment is a fragment defined in our JS code through @apollo/client's gql (it's defined in a JS file in the shared project which is shared between project1, project2 and project3).
My question would be: why does GraphQL Codegen complain about this fragment if it's defined in a JS file within the assets/shared/src project which comes before the other projects in the documents section of graphql-codegen.yml?
Does GraphQL Codegen disallow generating types this way?

visitFn.call is not a function is another error you can see from my output: I think (but I'm not sure) this one is related to @graphql-codegen/typescript-compatibility, as it seems that this plugin depends on graphql@15 (instead of 16).

What would be the correct way to migrate from Apollo Codegen in to GraphQL Codegen in this particular case?

Thank you!

@charlypoly
Copy link

Hi @antonvialibri1,

I am not certain that @graphql-codegen/typescript-compatibility is actually still required, could you try removing it and let me know?

@antonvialibri1
Copy link

antonvialibri1 commented Jul 29, 2022

Hi @charlypoly,

Thank you for your reply. I tried removing @graphql-codegen/typescript-compatibility, but I still get an error when running yarn graphql-codegen:

% yarn graphql-codegen   
yarn run v1.22.19
$ graphql-codegen --config graphql-codegen.yml
Cannot find module 'typescript'
Require stack:
- /Users/me/repositories/project/node_modules/ts-node/dist/util.js
- /Users/me/repositories/project/node_modules/ts-node/dist/index.js
- /Users/me/repositories/project/cosmiconfig-typescript-loader/dist/cjs/loader.js
- /Users/me/repositories/project//node_modules/cosmiconfig-typescript-loader/dist/cjs/index.js
- /Users/me/repositories/project/node_modules/graphql-config/index.js
- /Users/me/repositories/project/node_modules/@graphql-codegen/cli/cjs/graphql-config.js
- /Users/me/repositories/project/node_modules/@graphql-codegen/cli/cjs/config.js
- /Users/me/repositories/project/node_modules/@graphql-codegen/cli/cjs/codegen.js
- /Users/me/repositories/project/node_modules/@graphql-codegen/cli/cjs/generate-and-save.js
- /Users/me/repositories/project/node_modules/@graphql-codegen/cli/cjs/cli.js
- /Users/me/repositories/project/node_modules/@graphql-codegen/cli/cjs/bin.js
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

Here is the graphql-codegen.yml file:

schema: "./storage/app/lighthouse-schema.graphql"
documents:
  - assets/shared/src/**/*.js
  - assets/project1/src/**/*.js
  - assets/project2/src/**/*.js
  - assets/project3/src/**/*.js
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:
  ./assets/shared/__generated__/globalTypes.ts: # Equivalent for `--globalTypesFile` flag
    plugins:
      - typescript # Generates based types based on your schema
      #- typescript-compatibility # <------------------------------------- Removed `typescript-compatibility`
  ./assets/shared/__generated__:
    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
      #- typescript-compatibility # <------------------------------------- Removed `typescript-compatibility`

Should I also try removing typescript? Thank you.

P.S.: I left the package.json as-is:

// package.json
...
"dependencies": {
    "graphql": "^16.5.0",
    ...
},
"devDependencies": {
    "@graphql-codegen/typescript": "2.7.2",
    "@graphql-codegen/typescript-compatibility": "2.1.5",
    "@graphql-codegen/typescript-operations": "2.5.2",
    "@graphql-codegen/typescript-react-apollo": "3.3.2",
    "@graphql-codegen/typescript-document-nodes": "2.3.2",
    "@graphql-codegen/fragment-matcher": "3.3.0",
    "@graphql-codegen/typescript-graphql-files-modules": "2.2.0",
    "@graphql-codegen/introspection": "2.2.0",
    "@graphql-codegen/cli": "2.9.1",
    "@graphql-codegen/near-operation-file-preset": "2.4.0"
}
...

@antonvialibri1
Copy link

Tried removing typescript from the graphql-codegen.yml, still the same error.

Then I installed typescript as a dev dependency:

// package.json
...
"dependencies": {
    "graphql": "^16.5.0",
    ...
},
"devDependencies": {
    "@graphql-codegen/typescript": "2.7.2",
    "@graphql-codegen/typescript-compatibility": "2.1.5",
    "@graphql-codegen/typescript-operations": "2.5.2",
    "@graphql-codegen/typescript-react-apollo": "3.3.2",
    "@graphql-codegen/typescript-document-nodes": "2.3.2",
    "@graphql-codegen/fragment-matcher": "3.3.0",
    "@graphql-codegen/typescript-graphql-files-modules": "2.2.0",
    "@graphql-codegen/introspection": "2.2.0",
    "@graphql-codegen/cli": "2.9.1",
    "@graphql-codegen/near-operation-file-preset": "2.4.0",
    "typescript": "^4.7.4"
}
...

Re-ran yarn graphql-codegen and I still get the GraphQLDocumentError: Unknown fragment "UserContextFragment". error above.

I was wondering if GraphQL Codegen is able to understand that with this setup for documents:

documents:
  - assets/shared/src/**/*.js
  - assets/project1/src/**/*.js
  - assets/project2/src/**/*.js
  - assets/project3/src/**/*.js

I have a UserContextFragment defined within assets/shared/src/**/*.js shared project, then I refer to this fragment within project1, project2 and project3.

Apollo Codegen used to undestand that and would generate types for all queries/operations within project1, project2 and project3 that refer to UserContextFragment from shared.

Does GraphQL Codegen treat each documents entry separately and not as a whole? It seems to do it.

Thank you.

@charlypoly
Copy link

Hi @antonvialibri1,

sorry for the delay.

Does GraphQL Codegen treat each documents entry separately and not as a whole? It seems to do it.

GraphQL Code Generator does take documents as a whole before processing them.

I did an example project that tries to mimic your project structure and codegen configuration, and codegen is executing as expected: https://github.com/charlypoly/codegen-repros/tree/master/near-op-file-with-fragments

I would need much more information to help you migrate your project (ex: look at the content of this UserContextFragment definition).
Could you reach out to me using our chat/mail system via https://the-guild.dev/ 👀 ?

We'll be able there to interact more easily and faster, thank you!

@antonvialibri1
Copy link

UserContextFragment is defined in a file within assets/shared/src, so I guess it's included before all the other documents in the graphql-codegen.yml config file:

documents:
  - assets/shared/src/**/*.js # <---- This project contains UserContextFragment 
  - assets/project1/src/**/*.js
  - assets/project2/src/**/*.js
  - assets/project3/src/**/*.js
  

These are the contents of the assets/shared/contexts/UserContext.js file where that UserContextFragment is defined:

// assets/shared/contexts/UserContext.js

export const UserContextFragment = gql`
  fragment UserContextFragment on User {
    id
    user {
      firstname
      lastname
      email
    }
  }
`;

Then I have project1 and project3 which include that assets/shared/contexts/UserContext.js file, like this:

// assets/project1/src/App.js

import { UserContextFragment } from '@project/shared';

export const GET_USER_DATA = gql`
  query GetUserData {
    me {
      ...UserContextFragment
    }
  }
  ${UserContextFragment}
`;

...
// assets/project3/src/App.js

import { UserContextFragment } from '@project/shared';

export const GET_USER_DATA = gql`
  query GetUserData {
    me {
      ...UserContextFragment
    }
  }
  ${UserContextFragment}
`;

...

This file makes yarn graphql-codegen fail with the following error:

% yarn graphql-codegen                  
yarn run v1.22.19
$ graphql-codegen --config graphql-codegen.yml
✔ Parse Configuration
⚠ Generate outputs
  ❯ Generate ./assets/shared/__generated__/globalTypes.ts
    ✔ Load GraphQL schemas
    ✔ Load GraphQL documents
    ✖ GraphQL Document Validation failed with 2 errors;
      Error 0: GraphQLDocumentError: Unknown fragment "UserContextFragment".
      at /Users/me/project/assets/project1/src/App.js:4:10
      Error 1: GraphQLDocumentError: Unknown fragment "UserContextFragment".
      at /Users/me/project/assets/project3/src/App.js:4:10
  ✔ Generate to ./assets/shared/__generated__ (using EXPERIMENTAL preset "near-operation-file")
✨  Done in 3.87s.

@charlypoly
Copy link

Hi @antonvialibri1,

do you still get the error if you disable/comment out the near-operation-file preset?

@antonvialibri1
Copy link

Hi @charlypoly,

tried removing near-operation-file:

schema: "./storage/app/lighthouse-schema.graphql"
documents:
  - assets/shared/src/**/*.js
  - assets/project1/src/**/*.js
  - assets/project1/src/**/*.js
  - assets/project1/src/**/*.js
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:
  ./assets/shared/__generated__/globalTypes.ts: # Equivalent for `--globalTypesFile` flag
    plugins:
      - typescript # Generates based types based on your schema
#      - typescript-compatibility # <------------------------------------- Removed `typescript-compatibility`
  ./assets/shared/__generated__:
    #preset: near-operation-file # Tells codegen to generate multiple files instead of one # <------------ Commented out
    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
#      - typescript-compatibility # <------------------------------------- Removed `typescript-compatibility`

I still get the error though:

% yarn graphql-codegen
yarn run v1.22.19
$ graphql-codegen --config graphql-codegen.yml
✔ Parse Configuration
⚠ Generate outputs
  ❯ Generate ./assets/shared/__generated__/globalTypes.ts
    ✔ Load GraphQL schemas
    ✔ Load GraphQL documents
    ✖ GraphQL Document Validation failed with 2 errors;
      Error 0: GraphQLDocumentError: Unknown fragment "UserContextFragment".
      at /Users/me/project/assets/project1/src/App.js:4:10
      Error 1: GraphQLDocumentError: Unknown fragment "UserContextFragment".
      at /Users/me/project/assets/project3/src/App.js:4:10
  ❯ Generate ./assets/shared/__generated__
    ✔ Load GraphQL schemas
    ✔ Load GraphQL documents
    ✖ GraphQL Document Validation failed with 2 errors;
      Error 0: GraphQLDocumentError: Unknown fragment "UserContextFragment".
      at /Users/me/project/assets/project1/src/App.js:4:10
      Error 1: GraphQLDocumentError: Unknown fragment "UserContextFragment".
      at /Users/me/project/assets/project3/src/App.js:4:10
✨  Done in 5.02s.


@charlypoly
Copy link

@antonvialibri1 I just spotted a possible typo.

You shared your documents being the following:

documents:
  - assets/shared/src/**/*.js
  - assets/project1/src/**/*.js
  - assets/project1/src/**/*.js
  - assets/project1/src/**/*.js

The path to the shared project is assets/shared/src/**/*.js, however, you indicated that your UserContextFragment fragment is defined assets/shared/contexts/UserContext.js (which is not match by assets/shared/src/**/*.js) :

// assets/shared/contexts/UserContext.js

export const UserContextFragment = gql`
  fragment UserContextFragment on User {
    id
    user {
      firstname
      lastname
      email
    }
  }
`;

Is assets/shared/contexts/UserContext.js the correct path?
If so, the documents value should be changed to:

documents:
  - assets/shared/**/*.js
  - assets/project1/src/**/*.js
  - assets/project1/src/**/*.js
  - assets/project1/src/**/*.js

@antonvialibri1
Copy link

Hi @charlypoly,

oh, yeah, what a shame! 😅 I set those paths wrong in the GraphQL Codegen's config file.

I tried again and this config generates the types now 🎉 :

schema: "./storage/app/lighthouse-schema.graphql"
documents:
  - assets/shared/**/*.js
  - assets/project1/src/**/*.js
  - assets/project2/src/**/*.js
  - assets/project3/src/**/*.js
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:
  ./assets/shared/__generated__/globalTypes.ts: # Equivalent for `--globalTypesFile` flag
    plugins:
      - typescript # Generates based types based on your schema
#      - typescript-compatibility # <------------------------------------- Removed `typescript-compatibility`
  ./assets/shared/__generated__:
    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
#      - typescript-compatibility # <------------------------------------- Removed `typescript-compatibility`

The only thing that I noticed now is that the *.graphql.interface.ts files are generated inline with the related file (e.g. App.js component and App.graphql.interface.ts) instead of being generated all within the ./assets/shared/__generated__ folder.

Shouldn't the following configuration generate all files within ./assets/shared/__generated__:? If not, how can I generate all *.graphql.interface.ts files within that folder instead of having them inline with their related file?

  ./assets/shared/__generated__:
    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

Thank you!

@charlypoly
Copy link

Hi @antonvialibri1,

The types are generated in colocated files because you configured the near-operation-file preset.

Try removing the following lines:

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

@antonvialibri1
Copy link

Hi @antonvialibri1,

The types are generated in colocated files because you configured the near-operation-file preset.

Try removing the following lines:

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

Hi @charlypoly,

I tried removing those lines:

schema: "./storage/app/lighthouse-schema.graphql"
documents:
  - assets/shared/**/*.js
  - assets/project1/src/**/*.js
  - assets/project2/src/**/*.js
  - assets/project3/src/**/*.js
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:
  ./assets/shared/__generated__/globalTypes.ts: # Equivalent for `--globalTypesFile` flag
    plugins:
      - typescript # Generates base types based on your schema
  ./assets/shared/__generated__:
#    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

However, the outcome is just an empty assets/shared/__generated__/globalTypes.ts file being generated, without any types.

@charlypoly
Copy link

charlypoly commented Aug 31, 2022

@antonvialibri1,

can you replace ./assets/shared/__generated__: by ./assets/shared/__generated__/types.ts:?

@jviall
Copy link

jviall commented Sep 28, 2022

Hey all, what are the recommendations for configuring graphql-codegen to handle @client directives and cache-only fields?

@charlypoly
Copy link

Hey all, what are the recommendations for configuring graphql-codegen to handle @client directives and cache-only fields?

Hi @jviall,

We have a dedicated documentation page on this matter: https://www.the-guild.dev/graphql/codegen/docs/integrations/apollo-local-state

Let me know if you have any question, you can reach us through our Support chat available through the website.

@AK3030
Copy link

AK3030 commented Nov 1, 2023

outputFlat

With codegen, we recommend generated all types into a single file. When you want to generate more than one file (or, separate files based on operations/types), you can use presets feature. We currently have near-operation-file preset that has output similar to the default of apollo-tooling, but we don't have a preset that outputs something similar to outputFlat (should be simple to create, btw).

Is there a guide for creating presets? How would I go about doing this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests