dotansimha / dotansimha/graphql-code-generator

Allow plugin to extend the configuration object

Open
#9,295 0 comments 7 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
11.3k
Forks
1.4k
Avg merge
1d 1h
Merged PRs (30d)
23

Description

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

I want to have a plugin modify the current config/options object that will be passed down to the next plugins.
My goal is to use graphql directive on fields that will affect how these fields are generated.

More specifically I have the 2 following directives
```graphql
# Resolving a dataloaded field cannot use services other than dataloaders
directive @dataloaded on FIELD_DEFINITION
# Resolving a sync field cannot use services
directive @sync on FIELD_DEFINITION

type User {
id: Int! @sync # Cannot use services to resolve, has to be synchronous
name: String! @dataloaded # Can only use dataloaders, no direct access to database
items: [Item!] # Has full access to database and other services
}
```

In short, these directives allow me to document the schema on how the fields are resolved
I can then use this to statically analyse our queries to make sure we don't add new [N+1](https://medium.com/the-marcy-lab-school/what-is-the-n-1-problem-in-graphql-dd4921cb3c1a)s when these fields are queried inside a list
However, I need to ensure the resolver respects the contract, otherwise this is for nothing.

My solution is to have a plugin consume these directives before the `typescript-resolvers` plugin and inject the [`fieldContextTypes` ](https://the-guild.dev/graphql/codegen/plugins/typescript/typescript-resolvers#fieldcontexttypes) config
Depending on which directive is present, the resolver type will have a different context's type. I can also control if the ReturnType can be asynchronous with the following

```ts
type Impl = {
[field in keyof R]-?: field extends "__resolveType"
? R[field]
: R[field] extends Resolver | undefined
? (
parent: Data,
args: Args,
// prettier-ignore
context:
"Dataloaded" extends Context ? GraphQLContextOnlyLoaders
: "Synchronous" extends Context ? GraphQLContextSynchronous
: GraphQLContext,
info?: GraphQLResolveInfo,
) => "Synchronous" extends Context ? Ret : Promise | Ret
: never;
};
```

### Describe the solution you'd like

I'd like to add a new optional method on the graphql-codegen plugin that can modify the config/options after transforming documents but before executing the plugins

I'd add the following method:
```ts
export interface CodegenPlugin {
plugin: PluginFunction;
addToSchema?: AddToSchemaResult | ((config: T) => AddToSchemaResult);
validate?: PluginValidateFn;
+ extendConfiguration?: (schema: GraphQLSchema, options: {documents: Types.DocumentFile[], config: Types.PluginConfig, plugins: Types.ConfiguredPlugin[]}) => Promise | void;
}
```

and then run those here with

```ts
for (const plugin of pluginPackages) {
if (typeof plugin.extendConfiguration === 'function') {
await plugin.extendConfiguration(schemaInstance, options)
}
}
```

https://github.com/dotansimha/graphql-code-generator/blob/6621759cbb407c3e6d0702890a442505f80d118f/packages/graphql-codegen-core/src/codegen.ts#L130-L146

### Describe alternatives you've considered

The only other alternative I've found is to manually list the necessary config for the typescript-resolvers plugin in my config.
However, that would not be driven by the directive and I'd need an extra layer of verification to make sure they're aligned.
I don't want to have to duplicate the information and explode the config with an enormous list that impossible to maintain.

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

_No response_

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.