Graphql-code-generator: Generate types for the fields I actually select

Created on 11 Oct 2019  路  6Comments  路  Source: dotansimha/graphql-code-generator

Is your feature request related to a problem? Please describe.

Maybe my queries are too nested, but that's one of the powerful aspects of GraphQL. I'm coming from apollo-cli's client codegen, which will generate types for every complex/nested type that I'm specifying in my query. For example, a query like this:

query BlogpostQuery($filters: BlogPostFilters) {
    blogPosts(filters: $filters) {
        posts {
            author {
                name 
            }
            body
            datePosted
        }
    }
}

I would receive a few types without having to do any kind of nested referencing of types. These generated types have no reference to the "master" type from the schema, e.g. Pick[].

type BlogpostQuery_blogPosts_posts = {
    author: BlogPostsQuery_blogPosts_posts_author;
    body: string;
    datePosted: string;
}

type BlodpostQuery_blogPosts_posts_author = {
    name: string;
}

Describe the solution you'd like
What this would allow me to do is to simply import the type for the nesting level of the query that I want to work with at that moment (e.g. for defining a callback or a prop for a lower level component), rather than being forced to re-declare a type and "drill down" multiple levels from the parent type (which is defined using a series of nested Pick types).

Describe alternatives you've considered
We've had to try things in our project like:
type PostRecord = BlogPostsQuery["posts"][0];
But in our opinion this is verbose and unnecessary compared to simply importing a type that's going to be compiled away at build time anwyay (nothing to send to the client, little cost to generate and export).

Has this come up in discussions previously?

Most helpful comment

Hi @justin-capalbo
We had long discussion here about the differences. In the end, it's a matter of preference and what you are used to.

I can only recommend the following:

  • Use more fragments. Fragments makes it easier to understand what fields you actually need per each type in your schema, and the codegen generates types for fragments, so it makes it easier to reuse. It's also easier to pass fragment type to component props.

  • Create a file that does the following:

export * from './types.tsx';
import { MyQuery } from './types.tsx';

export const MyType = MyQuery['type'][number];

It will eliminate the amount of aliasing you have to do, and put them all in one place.

  • Use typescript-compatibility plugin. In an older version of the codegen we had those intermediate types, but we decided to drop it. this plugin creates those and might simplify the migrate for you.

All 6 comments

I realized I should have included this:

codegen.yml

overwrite: true
schema: https://path.to.graphql.schema
documents:
  - "src/**/*.tsx"
  - "!src/generated/**/*.*"
config:
  skipTypename: true
  withHooks: true
  withHOC: false
  withComponent: false
generates:
  ./src/generated/types.tsx:
    plugins:
      - typescript
      - typescript-operations
      - typescript-react-apollo

Hi @justin-capalbo
We had long discussion here about the differences. In the end, it's a matter of preference and what you are used to.

I can only recommend the following:

  • Use more fragments. Fragments makes it easier to understand what fields you actually need per each type in your schema, and the codegen generates types for fragments, so it makes it easier to reuse. It's also easier to pass fragment type to component props.

  • Create a file that does the following:

export * from './types.tsx';
import { MyQuery } from './types.tsx';

export const MyType = MyQuery['type'][number];

It will eliminate the amount of aliasing you have to do, and put them all in one place.

  • Use typescript-compatibility plugin. In an older version of the codegen we had those intermediate types, but we decided to drop it. this plugin creates those and might simplify the migrate for you.

Thank you for the response! Could you give an example for fragments that applies to my example above? Trying to visualize exactly what you mean. I don't have much personal experience with graphql fragments. Thank you for the suggestions!

@justin-capalbo sure!
Do something like that:

query BlogpostQuery($filters: BlogPostFilters) {
    blogPosts(filters: $filters) {
        posts {
            ...PostFields
        }
    }
}

fragment PostFields on Post {
  author {
    name 
  }
  body
  datePosted
}

Then, you'll get a type that represent PostFieldsFragment and you can reuse it.

I was thinking I'd switch to this lib over apollo-codegen, but the lack of intermediate/sub types is a showstopper for me. I use the pattern of grabbing the sub-type and using it in function definitions, sub-components, etc. extensively.

It's nice that it automatically exports the hooks, but it's not worth losing the sub-types to me.

Fragments is a hacky workaround, I don't want to keep defining fragments, I just want to use the types that are already defined

@markhalonen can you explain why fragments are a "hacky workaround"? it's part of the GraphQL spec, and this is the way to reuse and name field sets.
As we said before, if you wish to have intermediate types, add typescript-compatibility plugin.

Was this page helpful?
0 / 5 - 0 ratings