Graphql: Support for property resolvers for interface types

Created on 2 Oct 2019  路  8Comments  路  Source: nestjs/graphql

I'm submitting a...

[x] Feature request

Current behavior

I would like to create a resolver for an interface type which has a property resolver

Expected behavior

I should be able to define a resolver for an interface type and add property resolvers for virtual fields on that resolver. It should have access to services via constructor injection, etc.

Minimal reproduction of the problem with instructions

@InterfaceType()
export class IExample {
}

@Resolver(of => IExample)
export class ExampleResolver {
  constructor(
    private readonly example: ExampleService
  ) { }

  @ResolveProperty(type => Boolean)
  public async foo(@Root() item: IExample) {
    // this.example is available for this function
    return false
  }
}

What is the motivation / use case for changing the behavior?

If you have an interface type with multiple concrete types you want to be able to access it in gql like:

examples {
  foo
}

Not

examples {
  ... on ConcreteType {
    foo
  }
}

Which it seems like right now the only way to do it is to promote the field to resolvers for each concrete type.

Environment

Nest version: ^6.7.2
For Tooling issues:

  • Node version: v12.8.0
  • Platform: linux

All 8 comments

By the way the error I get when I try to do this is:

{
  "code": "GRAPHQL_VALIDATION_FAILED",
  "exception": {
    "stacktrace": [
      "GraphQLError: Cannot query field \"foo\" on type \"IExample\". Did you mean to use an inline fragment on \"ConcreteType\"?",
      "..."
    ]
  }
}

You can't query interfaces, you can only implement them.

EDIT:
It seems like I've misunderstood your question.
This is out of the scope (as it's not supported in the GraphQL spec)

It's not related to NestJS, but rather the GraphQL itself

I think you can, it clearly works for fields right on the object. I think that my problem may simply be that I didn't put the field on the interface though! I'll double check and follow up here.

Thanks.

@justinmchase nope you can't.
An interface in GraphQL is just like an interface in TS.
You need to implement a type with it before you can query it.
https://graphql.org/learn/schema/#interfaces

Well they're doing it even in that example you linked...

The character interface has a name field which you can then query without the concrete type. Of course the object you're returning has to match a concrete type in the union but you should be able to query fields on the interface type without having to use a concrete fragment.

interface Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
}
query HeroForEpisode($ep: Episode!) {
  hero(episode: $ep) {
    name # <-- this is a field on the interface
    ... on Droid {
      primaryFunction
    }
  }
}

Imagine you want the name field to be a resolver which is the same for all implementers of Character. I'll play with it a little later, I think I forgot to put the field on my interface and that's whats really blocking me.

Looking at this more, adding the field to the interface doesn't help. I am getting a crash in type-graphql which I'm filing a bug over there for but I do think that this is a legitimate issue in this library and think this ticket should be re-opened.

I think its legitimate because I am able to do this with graphql without using this library and it is documented in their documentation and it is a limitation of this library that this isn't supported.

I think the example from the gql documentation for characters that @marcus-sa linked to is a good one, so this feature request / bug becomes how can I implement Character.name as a property resolver?

@InterfaceType()
export class Character {
  @Field()
   public name: string
}

@Resolver(of => Character ) // <-- this crashes my app
export class CharacterResolver {
  constructor(
    private readonly characters: CharacterService
  ) { }

  @ResolveProperty(type => String)
  public async name(@Root() character: Character) {
    return this.charcters.resolveName(character)
  }
}

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings