Implementing 'Namespace-Typed' Resolvers in NestJS + GraphQL

Viewed 633

I stumbled across an interesting pattern for implementing GraphQL Mutations under their own "namespace", to help keep the Global Namespace neat and tidy. A regular implementation with Apollo would look something like this.

const typeDefs = gql`
type Mutation {
    article: ArticleMutations
  }

  type ArticleMutations {
    like: Boolean
    unlike: Boolean
  }
`;

const resolvers = {
  Mutation: {
    article: () => ({}), // ✨✨✨ magic! which allows to proceed call of sub-methods
  }
  ArticleMutations: {
    like: () => { /* resolver code */ },
    unlike: () => { /* resolver code */ },
  },
};

I decided to try and implement this pattern in a GraphQL API I am building with NestJS, however I seem to be hitting a stumbling block. I have created a TestResolver class, which contains a simple query, along with a namespaced-mutation that should return my nested resolvers.

@ObjectType()
export class TestMutations {
  constructor(private readonly _testService: TestService) {}

  @Field(() => String)
  test: string;

  @ResolveField(() => Unit, {})
  async create(@Args('input') input: CreateTestInput): Promise<Test> {
    try {
      return await this._testService.create(input);
    } catch (err) {
      this._logger.error(err);
      throw new HttpException('An error occurred when creating the Test', HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }
}

@Resolver(() => Test)
export class TestResolver {
  constructor(private readonly _testService: UnitService) {}

  @Query(() => Test, { name: 'test', nullable: true })
  async getTest(@Args('id') id: Id): Promise<Test> {
    try {
      return await this._testService.get(id);
    } catch (err) {
      this._logger.error(err);
      throw new HttpException('An error occurred when fetching the Test', HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  @Mutation(() => TestMutations)
  test(): TestMutations {
    return new TestMutations(this._testService);
  }
}

Through some trial and error I managed to get NestJs to successfully build the above code, however I cannot get the nested FieldResolvers to show up.

enter image description here

The plain Apollo implementation of this pattern does seem like it uses some "magic" code to achieve it, so it's definitely possible that the structure of NestJS will not allow me to do something like this. Does anyone have any ideas?

1 Answers

You can do this with Nest.js too. Here how i did it:

  1. I created root resolver.

@Resolver('root')
export class RootResolver {
  @Query((returns) => KBQuery, { name: 'kb' })
  kbQuery() {
    return new KBQuery();
  }

  @Query((returns) => UserQuery, { name: 'user' }) // Here can be @Mutation
  userQuery() {
    return new UserQuery();
  }
  // ... Other root Queries and Mutations here
}

  1. Then i create child resolvers (for queries or mutations) like this:

@ObjectType()
export class UserQuery {}

@Resolver((of) => UserQuery)
export class UserQueryResolver {
  @ResolveField((returns) => UserMyPayload)
  async me(@Context('req') { currentUser }: Request) {
    if (!currentUser) return { error: new ApiError('unauthorized', 'Authorization required') };

    return { user: currentUser.user };
  }
}

  1. So now i can make query like this: user.me to get user details.

I wish it will be more easier to do with Nest.js. So guys from Nest need to add something in @Query/@Mutation attribute.

Related