Search code examples
graphqlnestjsapollo-serverapollo-federation

NestJS GraphQL Apollo Federation many-to-many ResolveField is not triggered


I have 3 services, which are technically completely separate from each other:

  • User
  • Channel
  • UserChannelMembership

Each service defines its own subgraph. User does not refer Channel, Channel does not refer User. The only service which refers both is UserChannelMembership. User <-> Channel is M:M; UserChannelMembership is an actual service, which stores this relations data.

I use Apollo Federation to generate supergraph. Schema generated is good and I am able to obtain all I need except for the User/Channel fields I extend in the UserChannelMembership service [clarification: schema has the fields, the data is null]. Technically @ResolveField() functions are never called. I tried to put a test @Query inside the same class, and it works. So the resolver file itself is fine. It's either something I don't understand or I miss or a bug (less likely).

Schema files and the resolver code are below.

The question itself is the following: I need UserChannelMembership service to extend the base User and Channel GraphQL types (defined initially in User and Channel services) and provide usersPerChannel and channelsPerUser fields, which will deliver the data provided by corresponding functions in UserChannelMembership service.

user.types.graphql (Defined in User service)

type User @key (fields: "id"){
  id: ID!
  username: String!
}

channel.types.graphql (Defined in Channel service)

type Channel @key (fields: "id"){
  id: ID!
  channelname: String!
}

user-channel-members.graphql (Defined in UserChannelMembership service)

extend type User @key(fields: "id") {
  id: ID! @external
  channelsPerUser: [Channel]
}
extend type Channel @key(fields: "id") {
  id: ID! @external
  usersPerChannel: [User]
}

user-channel-members.resolver.ts

@Resolver('User')
// also tried:
// @Resolver('UserChannelMembers')

export class UserChannelMembersResolver {

// this query works if I define type Query
//  @Query('userChannelMembers')
//  findAll() {...removed...}
//

// this never got called
  @ResolveField()
// also tried:
// @ResolveField('channelsPerUser')
  findChannelsPerUser(@Parent() user: any) {
    ...never called...
  }
}

Solution

  • Final answer is the following.

    Doesn't work:

    export class UserChannelMembersResolver {
      @Resolver('User')
      @ResolveField('channelsPerUser')
      async channelsPerUser(@Parent() user: any) {...}
    
      @Resolver('Channel')
      @ResolveField('usersPerChannel')
      async usersPerChannel(@Parent() channel: any) {...}
    }
    

    Works: (first file)

    @Resolver('User')
    export class UserResolver {
    
      @ResolveField('channelsPerUser')
      async channelsPerUser(@Parent() user: any) {...}
    }
    

    (second file)

    @Resolver('Channel')
    export class ChannelResolver {
    
      @ResolveField('usersPerChannel')
      async usersPerChannel(@Parent() channel: any) {...}
    }
    

    Then import both as a provider in a module and that's it.