eddeee888 / graphql-code-generator-plugins

List of GraphQL Code Generator plugins that complements the official plugins.
MIT License
51 stars 12 forks source link

Disabling auto generation of object type resolvers #275

Closed markbussard closed 4 months ago

markbussard commented 4 months ago

I have been working on migrating a decently sized backend with this tool, and have a fair amount of object types defined across all of our modules. However, we do not need the automatic generation of resolvers for the majority of our defined object types with the exception of a few (there's around 170 currently generated which are essentially unused). And I noticed in the last couple weeks you added a new release which included having more control over which types of files are generated via the resolverGeneration config option.

And I know this new option comes with the capability to include glob patterns in order to specify which resolver files we want generated. However, the issue is that a lot of the types we have defined don't really have any sort of pattern that can be associated by a glob.

So, I'm wondering if currently the best way around this problem is to simply provide the glob patterns for the couple object types we do want generated resolvers for? I.e. something like:

resolverGeneration: {
  query: '*',
  mutation: '*',
  subscription: '*',
  scalar: '*',
  object: ['*.Workflow', '*.User', ...],
  union: '',
  interface: ''
}

And the above works fine, but it just seems counterproductive in regard to what this tool is providing where other developers don't have to worry about touching the graphql-codegen config file when making schema updates / defining a new module and types.

eddeee888 commented 4 months ago

Hello @markbussard,

Thank you for using the tool and pushing it to the limit to find DevX issues πŸ™ And you are right, the intention of this preset is to help users avoid touching config file whilst making easy to focus on the main logic.

In 0.8.0, telling codegen which file to create is unfortunately the best way.


However, I'm working on a new resolverGeneration option called minimal. This is intended for users who are familiar with GraphQL, Server Preset and know exactly which object type they want to create.

This option translates to this:

resolverGeneration: {
  query: '*',
  mutation: '*',
  subscription: '*',
  scalar: '*',
  object: '', // (*)
  union: '',
  interface: ''
}

(*) Note we don't generate any object type files by default. This will create a lot less boilerplate types.

There are some upcoming features that will make this mode usable:

  1. Force creating object type if there's an associated mapper

    • This means when you create an object type mapper, it can be removed from the resolverGeneration.object list.
    • Note: I'm doing a few finishing touches to give just enough information to the user without overwhelming them
  2. Auto object type resolver wireup (WIP): With this feature, any files that is manually created that is in the right place will be automatically added to resolvers.generated.ts on running codegen. For example:

# src/schema/user/schema.graphql
extend type Query {
  user(id: ID!): User
}

type User {
  id: ID!
  name: String!
}

With resolverGeneration: 'minimal', only src/schema/user/resolvers/Query/user.ts is created and wired up to resolvers.generated.ts

If you need User type created, you can:

  1. (Very early idea / far future) To help users knowing which file path they can create in (2.), I'm planning to create a tool (Maybe CLI or VSCode extension) to create the file on click.

I feel that (1.) and (2.) may help your use case. But if you have ideas, I'm more than happy to discuss!

markbussard commented 4 months ago

Hi @eddeee888,

Thank you for the quick response!

First off, I did mess around with the config some more last night and found using the externalResolvers option is also another way around my issue, where I defined it like so:

externalResolvers: {
  User: 'user/resolvers/User#User',
  Project: 'project/resolvers/Project#Project',
  Workflow: 'workflow/resolvers/Workflow#Workflow',
  ...
}

And the above does seem like slightly more work than what I had before (in terms of writing a couple more letters out), but I think this way it looks a bit cleaner / less funky with the globs.

But in regard to your proposals / WIP: (2) will certainly fix my issue, and for me, definitely makes sense and is intuitive. In fact, I initially tried this exact approach yesterday after I had removed all of my unused object type resolvers and ran codegen again. It seemed natural to me that if I still had a file for my User type, that this would be picked up and it would be included in the generated resolvers file.

Also, I haven't started adding mappers quite yet, but there will be a few getting added in the future, so (1) will also be huge and a very helpful addition.

And do you have a rough estimate of when you think these new features would get added in? πŸ˜…

Lastly, just wanted to say thank you for this tool πŸ™ It has been extremely helpful in this initial migration process and I look forward to continuing to build and expand with it.

eddeee888 commented 4 months ago

Hi @markbussard ,

Thank you! I really appreciate your feedback and hear you are getting value out of this preset πŸ™

(1) is now available in @eddeee888/gcg-typescript-resolver-files@0.8.1 βœ… (2) is my top priority at the moment as there's a few similar feedback recently. 🚧

I can't give a definite timeline yet but this is my focus for the server preset. (I'm aiming to have it ready within 2 weeks but let's see)

eddeee888 commented 4 months ago

Hi @markbussard , good news!

I've got an alpha version:

yarn add -D @eddeee888/gcg-typescript-resolver-files@pr281-run503-1
  1. You can now try the minimal mode:
defineConfig({
  resolverGeneration: "minimal",
})
  1. The server preset now automatically detects files at the right location, and wire them up to resolvers.generated.ts. Here's the summary again:
    • You can create an empty file at the right location, run codegen, and the type import declaration and variable statement will be automatically added
    • This means existing files created by resolverGeneration: 'recommended' mode is wired up by default. You can remove them and see them go away from resolvers.generated.ts.
    • Object types with mappers are generated with a comment explaining why it is created.

Please try the alpha version and let me know how it goes! In the meantime I'll finish writing tests and work towards a v0.9 release for this feature.

markbussard commented 4 months ago

Hi @eddeee888

I just tried this out and mostly everything seems to be working appropriately. I removed the externalResolvers config option I had previously defined, ran codegen, and all those object type resolvers were still included in the resolvers.generated.ts file.

I then tried adding a file for another random type we had defined, ran codegen, and the skeleton code for that object type resolver was generated and it was added to the resolvers.generated.ts file.

I then made a schema.mappers.ts for my user module and defined a UserMapper interface accordingly with how the data is structured in our DB, deleted the existing User.ts file we had, ran codegen, and saw the User resolver and child resolver skeleton functions were defined and the comments explaining why each was generated. One thing I will note here is in regard to the time codegen took to run after adding the mapper. Before the mapper, it took around 6s. With the mapper, it takes 14-15s, over double what it took previously. And that does worry me a bit for adding additional mappers.

And then the only other issue I saw was in regards to the resolver files generated for custom scalar types.

I had a custom scalar defined in my base module with the file already generated and logic included. And I'll use the following as an example:

# src/schema/base/schema.graphql
scalar CustomScalar

I run codegen and look at my CustomScalar.ts which again, was already generated previously and there is now an additional line added:

// src/schema/base/resolvers/CustomScalar.ts
import { GraphQLScalarType } from 'graphql'
// new duplicate import added
import { GraphQLScalarType } from 'graphql'

export const CustomScalar = new GraphQLScalarType({
  ...
})

So, now I have two imports. And if I run codegen again, it will add the same import again, and it will repeat this behavior every time I run codegen

eddeee888 commented 4 months ago

One thing I will note here is in regard to the time codegen took to run after adding the mapper. Before the mapper, it took around 6s. With the mapper, it takes 14-15s, over double what it took previously. And that does worry me a bit for adding additional mappers.

Interesting! Could you try disabling static analysis using fixObjectTypeResolvers: 'disabled' to see if the speed gets back to normal? Note that this will stop doing mapper vs schema types comparison. But if this is the issue, I know where to focus to improve performance. However, I don't think this performance fix would be in this release.

So, now I have two imports. And if I run codegen again, it will add the same import again, and it will repeat this behavior every time I run codegen

Thank you for finding the bug. I'll fix this and do another alpha release.

eddeee888 commented 4 months ago

Hi @markbussard , could you try this new alpha version please:

yarn add -D @eddeee888/gcg-typescript-resolver-files@pr281-run507-1

This should fix the double import line issue for custom scalars. If this is ok, I'll make this version v0.9. I'll bring the performance discussion to another issue to track.

markbussard commented 4 months ago

One thing I will note here is in regard to the time codegen took to run after adding the mapper. Before the mapper, it took around 6s. With the mapper, it takes 14-15s, over double what it took previously. And that does worry me a bit for adding additional mappers.

Interesting! Could you try disabling static analysis using fixObjectTypeResolvers: 'disabled' to see if the speed gets back to normal? Note that this will stop doing mapper vs schema types comparison. But if this is the issue, I know where to focus to improve performance. However, I don't think this performance fix would be in this release.

So, now I have two imports. And if I run codegen again, it will add the same import again, and it will repeat this behavior every time I run codegen

Thank you for finding the bug. I'll fix this and do another alpha release.

I tried this option and it didn't really do anything for the speed, it's still the same unfortunately.

No worries though, since it's obviously outside the scope of this issue. Just thought I'd bring it up after testing it.

markbussard commented 4 months ago

Hi @markbussard , could you try this new alpha version please:

yarn add -D @eddeee888/gcg-typescript-resolver-files@pr281-run507-1

This should fix the double import line issue for custom scalars. If this is ok, I'll make this version v0.9. I'll bring the performance discussion to another issue to track.

Awesome, the issue has been fixed with this new version.

Thank you πŸ™Œ

eddeee888 commented 4 months ago

Resolvers auto wireup and minimal generation mode is now available in v0.9.0.

Thank you for reporting and collaborating with me to move things forward!

eddeee888 commented 1 month ago

Hi @markbussard ,

FYI, I've released an alpha version to hopefully fix the perf issue as mentioned in this comment: https://github.com/eddeee888/graphql-code-generator-plugins/issues/282#issuecomment-2273440171

If you have some time, please try it out and let me know πŸ™


The reason is that files are being sent to the codegen engine to generate even if they are not changed. So, we now selectively pick only the files that require generation.

Improvements depends on the codebase but I'm seeing: