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

Automatic Field Resolver (High Order Field Resolvers) #385

Closed
david-eos opened this issue Jul 29, 2019 · 4 comments
Closed

Automatic Field Resolver (High Order Field Resolvers) #385

david-eos opened this issue Jul 29, 2019 · 4 comments
Labels
Question ❔ Not future request, proposal or bug issue Solved ✔️ The issue has been solved

Comments

@david-eos
Copy link

david-eos commented Jul 29, 2019

I've been noticing that many field resolvers derivated from relations use to be simple. Simple enough to be able to resolve them using a simple query with TypeORM query builder. It automatically creates the query based on the relation decorators of fields of entity objects.

This is an example:

@Resolver(Post)
export class PostResolver {

    @FieldResolver()
    public author(@Root() post: Post): Promise<Person> {
        return getManager()
            .createQueryBuilder()
            .relation(Post, "author")
            .of(post)
            .loadOne();
    }

}

What I'm trying to say is: I think we should be able to resolve relation fields automatically using TypeORM query builder so that we don't have to code a lot of simple field resolvers. I think Vesper has this feature.

At least I think it would be amazing if we could somehow create high order field resolver dynamically, so we can create base resolvers that creates all field resolvers depending on the entity.

Maybe something like this:

export function createBaseFieldResolver<T extends ClassType, X extends ClassType>(
    field: string,
    arrayField: string,
    classType: T,
) {

    @Resolver({ isAbstract: true })
    abstract class BaseFieldResolver {

        @FieldResolver({ name: `${field}` })
        protected async singleFieldResolver(@Root() single: T): Promise<any> {
            return getManager()
                .createQueryBuilder()
                .relation(classType, `${field}`)
                .of(single)
                .loadOne();
        }

        @FieldResolver({ name: `${arrayField}` })
        protected async arrayFieldResolver(@Root() multiple: T): Promise<any[]> {
            return getManager()
                .createQueryBuilder()
                .relation(classType, `${arrayField}`)
                .of(multiple)
                .loadMany();
        }
    }

    return BaseFieldResolver;
}

The problem is that this only works for two fields, and I don't find any way to programmatically create a field resolver function for each relation field depending on the class type. Is it possible?

I'm new to TypeGraphQL, so please be patient. And sorry for my bad english.

@MichalLytek
Copy link
Owner

MichalLytek commented Jul 29, 2019

I think that this will be covered by #44

I think Vesper has this feature.

It uses TypeORM entities metadata to create resolvers for joining relations.

@MichalLytek MichalLytek added the Question ❔ Not future request, proposal or bug issue label Jul 29, 2019
@david-eos
Copy link
Author

I think that this will be covered by #44

I've been looking that issue. So the plan is resolving field automatically developing a typeorm helper plugin using a dataloader so queries are much less and optimized? Correct me if I'm wrong please.

It uses TypeORM entities metadata to create resolvers for joining relations.

Yes, it's very nice. I was using Vesper before, but I changed to use TypeORM + TypeGraphQL because of that double decorator entity definition and auto schema generation that I fell in love with. Great job by the way!

However, I miss automatic resolver functionality. I hope that comes soon!

What about being able to create high order dynamic field resolver? I suggest adding a @field handler option so that handler can create dynamically it correspondent resolver and many others.

Let me explain the idea, entity will look like this:

@ObjectType()
@Entity()
export class Post extends BaseEntity {

    @Field(() => Int)
    @PrimaryGeneratedColumn()
    public postId: number;

    @Field(() => Int)
    @Column()
    public authorId: number;

    @Field()
    @Column()
    public name: string;

    @Field(() => Person, { handler: singleFieldResolver })
    @ManyToOne(() => Person, (person) => person.posts)
    @JoinColumn([
        {
            name: "authorId",
            referencedColumnName: "personId",
        },
    ])
    public author: Person;

    @Field(() => [Person], { handler: arrayFieldResolver })
    @ManyToMany(() => Person)
    public mentioned: Person[];
}

And this would be the high order resolvers that would create their resolvers:

        protected async singleFieldResolver(field: string, @Root() single: T): Promise<any> {
            return getManager()
                .createQueryBuilder()
                .relation(classType, field)
                .of(single)
                .loadOne();
        }

        protected async arrayFieldResolver(field: string, @Root() multiple: T): Promise<any[]> {
            return getManager()
                .createQueryBuilder()
                .relation(classType, field)
                .of(multiple)
                .loadMany();
        }

Again, I don't know even if this or similar is possible.

@MichalLytek
Copy link
Owner

This should work:

@Resolver(of => Post)
export class PostResolver extends createBaseResolver(Post) {}

For multiple relations, you would need something like this:

@Resolver(of => Post)
export class PostResolver
  extends relationResolver(Post, "field1", 
 	relationResolver(Post, "field2", 
  		relationResolver(Post, "field3", class {})
  )
) {}

You would need to use the mixins pattern #359.

@MichalLytek
Copy link
Owner

Closing for a housekeeping purposes 🔒

@MichalLytek MichalLytek added the Solved ✔️ The issue has been solved label Jul 29, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Question ❔ Not future request, proposal or bug issue Solved ✔️ The issue has been solved
Projects
None yet
Development

No branches or pull requests

2 participants