Type-graphql: [Feature request] Resolvers for interfaces

Created on 23 Feb 2019  路  10Comments  路  Source: MichalLytek/type-graphql

Is your feature request related to a problem? Please describe.
Currently resolvers can only be applied to ObjectTypes

Describe the solution you'd like
If an InterfaceType is specified in the params to a resolver, all @FieldResolvers on the interface will apply to all ObjectType implementations.

If a resolver for an ObjectType already has a FieldResolver for a select field, it should use the ObjectType's definition (instead of the InterfaceType)

@InterfaceType()
class Test {
  @Field() firstName: string
  @Field() lastName: string
  @Field() fullName: string
}

@ObjectType({ implements: Test })
class Test2 extends Test {}

@ObjectType({ implements: Test })
class Test3 extends Test {}

@Resolver(of => Test)
class TestResolver {
  @FieldResolver()
  fullName(@Root() test: Test) {
    return test.firstName + test.lastName
  }
}
Community Enhancement

Most helpful comment

Closed by #579, released in v0.18.0-beta.14 馃帀

@samdenty @chrisdostert @justinmchase @hyperloris @dmks
Let me know guys how it works for you 馃槈
I would like to roll out the stable release soon but I can't do that without confirmation on this feature.

All 10 comments

Or alternatively a way to compose resolvers

@Resolver()
class TestResolver {
  @FieldResolver()
  fullName(@Root() test: Test) {
    return test.firstName + test.lastName
  }
}

@Resolver(of => Test2)
class Test2Resolver extends TestResolver {}
...

I've always treated GraphQL interface like a normal TS interface, that is pure abstract and doesn't have an implementation. I thought that it's better to just use a base object type for fields with resolver and args and just extend them.

@ObjectType()
class Person {
  @Field()
  age: number;
}

@ObjectType()
class Student extends Person {
  @Field()
  universityName: string;
}

Also, mixing the extending class type is prohibited - your object type can't extend an interface type:
https://19majkel94.github.io/type-graphql/docs/interfaces-and-inheritance.html

Note that both the subclass and the parent class must be decorated with the same type of decorator, like @ObjectType() in the example Person -> Student above. Mixing decorator types across parent and child classes is prohibited and might result in schema building error - you can't e.g decorate the subclass with @ObjectType() and the parent with @InputType().

When graphql-js calls the field resolver of the InterfaceType? For common interface field of the interface return type of the query? Or it always delegate the field resolving to the proper ObjectType and just ignores the interface resolvers?

Inheriting resolvers looks like a non standard solution that has to be handled specially:
https://github.com/apollographql/graphql-tools/pull/720

@19majkel94 How about allowing for composition of resolvers at the bare minimum? Doesn't go against the graphql-js spec, just a way of increasing code-reuse

@InterfaceType()
class Test {
  @Field() firstName: string
  @Field() lastName: string
  @Field() fullName: string
}

@ObjectType({ implements: Test })
class Test2 extends Test {}

@ObjectType({ implements: Test })
class Test3 extends Test {}

@Resolver()
class TestResolver {
  @FieldResolver()
  fullName(@Root() test: Test) {
    return test.firstName + test.lastName
  }
}

@Resolver(of => Test2)
class Test2Resolver extends TestResolver {}

@Resolver(of => Test3)
class Test3Resolver extends TestResolver {}

Have you tried that?

How about allowing for composition of resolvers at the bare minimum?

It's perfectly legal to have a field resolver in base resolver class:

https://github.com/19majkel94/type-graphql/blob/526886c9cf0e2a951e0ed5fe45d8d00ed4fc3675/examples/resolvers-inheritance/resource/resource.resolver.ts#L54-L58

ah didn't know that was possible thanks

As graphql-tools supports inheritResolversFromInterfaces, it might be a good idea to support this kind of feature too.

Of course after the #261 arrives.

@MichalLytek Not having this breaks paginating any field which is an interface type (w/ multiple implementations) in relay. Relay only allows paginating a single connection within a fragment. If you can't define the connection on the interface type then you can't paginate it. Side note; in the spirit of getting this rolled with some priority, would donating some money help? I don't have time to roll it myself right now and it seems like you're not satisfied with the implementation @kl4n4 did.

@chrisdostert If you need this urgently, you can just use his branch of the fork he made.

My priority is vNext when the internal architecture is made properly and I can add features like this easily and quickly 馃槈

Closed by #579, released in v0.18.0-beta.14 馃帀

@samdenty @chrisdostert @justinmchase @hyperloris @dmks
Let me know guys how it works for you 馃槈
I would like to roll out the stable release soon but I can't do that without confirmation on this feature.

@MichalLytek Ok cool, so you can define the resolver for the field either on the interface itself or on the resolver?

https://github.com/MichalLytek/type-graphql/pull/579/files#diff-b81ada8bdad46121b8aa68dd67d9238aR145

So yeah that seems like it would work perfect because I would need access to services and the resolver will be able to have service dependencies resolved I believe. So that all looks great!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

tongtwist picture tongtwist  路  3Comments

MichalLytek picture MichalLytek  路  3Comments

maplesteve picture maplesteve  路  3Comments

MichalLytek picture MichalLytek  路  4Comments

limenutt picture limenutt  路  3Comments