nestjs / graphql

GraphQL (TypeScript) module for Nest framework (node.js) 🍷
https://docs.nestjs.com/graphql/quick-start
MIT License
1.46k stars 396 forks source link

federated graph: interface field resolvers create schema but routing seems to fail #2464

Closed Mporsi closed 2 years ago

Mporsi commented 2 years ago

Is there an existing issue for this?

Current behavior

This interface:

@InterfaceType({
  resolveType: (value: { __typename: string }) => value.__typename,
})
export abstract class IVoteable {
  @Field(() => ID)
  id: string;
}

Has the following resolver:

@Resolver(() => IVoteable)
export class VoteableResolver {
  constructor(private readonly voteService: VoteService) {}

  @Mutation(() => VotingResult)
  createVote(
    @Args('subjectId') subjectId: string,
    @Args('userId') userId: string,
    @Args('subjectType', {
      type: () => Voteables,
    })
    subjectType: Voteables,
    @getUserIp() ip: string
  ) {
    return this.voteService.create({ subjectType, subjectId, userId, ip });
  }

  @Mutation(() => VotingResult)
  removeVote(
    @Args('subjectId', { type: () => String }) subjectId: string,
    @Args('userId') userId: string
  ) {
    return this.voteService.remove({ subjectId, userId });
  }

  @ResolveField(() => Int, {})
  async voteCount(@Parent() voteable: IVoteable) {
    return await this.voteService.getVoteCount(voteable.id);
  }

  @ResolveField(() => Boolean, {})
  async hasUserVoted(
    @Parent() subject: IVoteable,
    @Args('userId', {
      type: () => ID,
    })
    userId: string
  ) {
    return await this.voteService.hasUserVotedOnSubject(subject.id, userId);
  }

  @ResolveField(() => [Vote], {})
  public async getAllVotesOnASubject(@Parent() subject: IVoteable) {
    return await this.voteService.findAllVotesBySubject(subject.id);
  }
}

however when I have an entity implement the interface:

@ObjectType({
  implements: () => [IVoteable],
})
@Directive('@extends')
@Directive('@key(fields: "id")')
export class Subject implements IVoteable {
  @Field(() => ID)
  @Directive('@external')
  id: string;
}

The subgraph is generated and the schema is merged alright into the super graph. But when querying the @ResolveField ones they return null and the resolver is never hit. I have been running a debug on both the gateway and the service, I get nothing particularly useful from the error on the gateway.

However if I turn the interface resolver into an abstract resolver like:

@Resolver(() => IVoteable, { isAbstract: true })
export abstract class BaseVoteableResolver {
  constructor(private readonly voteService: VoteService) {}
.
.
.
}

and create a subject resolver like:

@Resolver(() => Subject)
export class SubjectResolver extends BaseVoteableResolver {}

Then it works as intended although not be resolving the interface but by extending the subject type, which is not the intention.

I don't know if this is intentional, but it seems to me like a bug.

Minimum reproduction code

nan

Steps to reproduce

No response

Expected behavior

I expect that when I create an interface and an interface resolver, it will be hit when querying.

Package version

2.2.0

Graphql version

"@nestjs/graphql": "^10.1.3",
"@apollo/client": "3.7.0",
"@apollo/federation": "0.37.1",
"@apollo/gateway": "0.46.0",
"@apollo/subgraph": "0.5.1",
"apollo-datasource-rest": "^3.7.0",
"apollo-link-retry": "2.2.16",
"apollo-server-express": "3.10.3",
"apollo-server-plugin-response-cache": "^3.7.1",
"apollo-upload-client": "17.0.0",
"graphql": "15.8.0",
"graphql-config": "4.3.6",
"graphql-tools": "8.3.6",
"graphql-upload": "13.0.0",

NestJS version

9.1.4

Node.js version

16.15.0

In which operating systems have you tested?

Other

No response

kamilmysliwiec commented 2 years ago

Please, use our Discord channel (support) for such questions. We are using GitHub to track bugs, feature requests, and potential improvements.