eddeee888 / graphql-code-generator-plugins

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

[FEAT] Generate interfaces based on intersection of mappers and typescript-resolvers #340

Open matt-potter opened 2 weeks ago

matt-potter commented 2 weeks ago

I'd be great if we could have an 'interface only' mode that just augments the interfaces from typescript-resolvers to mark the required fields as required based on what's in the mapped types (equivalent to current minimal mode) and leaves all others optional. That, run in merged mode, would give me a perfect minimal setup as far as I can tell.

Thanks!

eddeee888 commented 2 weeks ago

Hi @matt-potter , could you help me understand this feature request by providing a small example please? 🙏

matt-potter commented 1 week ago

Sure, here's a repo with a basic example using your package in disabled mode for resolver generation.

Essentially, I want to ensure the creation of resolvers where they are required, but I don't want to codegen the actual functions - i just want a set of interfaces to implement

What I'm hoping for is just to augment the existing interfaces generated by typescript-resolvers, making only the missing properties required and leaving the rest as optional - something like this.

import type { AuthorResolvers } from "./../graphql/generated/types.generated.js";

export interface RequiredAuthorResolvers extends AuthorResolvers {
    books: NonNullable<AuthorResolvers["books"]>;
}

const resolvers: RequiredAuthorResolvers = {
    books: async (_parent, _arg, _ctx) => {
        /* Author.books resolver is required because Author.books exists but AuthorMapper.books does not */
    },
};

The only other options currently (afaik) are to specify avoidOptionals to true and implement all fields, or to false and potentially miss resolvers

matt-potter commented 1 week ago

for what it's worth, what I'm describing is how Golang's gqlgen operates. Putting in the same schema and mapping equivalent structs generates these interfaces to implement - you only need to implement what's required, but still have the option to override existing properties.

type AuthorResolver interface {
    Books(ctx context.Context, obj *model.Author) ([]*model.Book, error)
}
type BookResolver interface {
    Author(ctx context.Context, obj *model.Book) (*model.Author, error)
}
type QueryResolver interface {
    Books(ctx context.Context) ([]*model.Book, error)
    Book(ctx context.Context, id string) (*model.Book, error)
    Authors(ctx context.Context) ([]*model.Author, error)
    Author(ctx context.Context, id string) (*model.Author, error)
}
eddeee888 commented 1 week ago

Ah, understood 👍 Thanks for the detailed examples.

I think it's doable and can be implemented in the future. However, it will require some work, both in the base codegen plugin (to have non-optional config at the granular field level ) and in the preset (improved static analysis logic).