graphql-compose / graphql-compose-mongoose

Mongoose model converter to GraphQL types with resolvers for graphql-compose https://github.com/nodkz/graphql-compose
MIT License
708 stars 94 forks source link

How to use internal mechanisms for dynamic population/projection for custom resolver #321

Open tasoskakour opened 3 years ago

tasoskakour commented 3 years ago

Hello!

Let's suppose that we've got the below 2 resolvers:

{
   userById: UserTC.mongooseResolvers.findById(),
   userGetMe: UserTC.addResolver({
      name:"userGetMe",
      args: {},
      type: User,
      resolve: (_,__,context) => {
           const {auth} = context;
           const {myId} = auth;
           return User.findById(myId); 
      }, 
   }).getResolver("userGetMe").withMiddlewares([getMyAuthId])
}

As you can see both of the resolvers above eventually utilize findById of mongoose. The only difference is that userGetMe resolver does not take any arguments but returns the User with the id of the current authenticated user.

The solution above is ideal and not realistic. In reality I have to manually populate any nested fields in the User.findById(myId) call, based on the fields request inside the graphql query. So basically I need to write helper functions like what you've got here in order to achieve my result.

My question is, is there a clever way to overcome this? Is there a way for the userGetMe resolver to eventually use the UserTC.mongooseResolvers.findById() resolver to fetch the User fields correctly populated, projected etc?

yurtaev commented 3 years ago

You can try to reuse findById:

schemaComposer.Query.addFields({
  ...requireMiddlewares([authMiddleware, permissionCustomerMiddleware], {
    me: SysUserTC.mongooseResolvers
      .findById({ lean: true })
      .setDescription("Current User's Profile")
      .removeArg("_id") // <=== remove arg _id
      .wrapResolve((next) => (rp) => {
        const { userId } = rp.context.request;
        rp.args._id = userId
        return next(rp);
      }),

    sysUserById: SysUserTC.mongooseResolvers.findById({ lean: true }),
  }),
})
tasoskakour commented 3 years ago

Thanks for the reply @yurtaev, it worked!

Quick question regarding the requireMiddlewares, why aren't you using withMiddlewares? Just personal preference?

yurtaev commented 3 years ago

requireMiddlewares just helper

/**
 * Add middlewares to resolvers
 * https://graphql-compose.github.io/docs/api/Resolver.html#withmiddlewares
 */
export const requireMiddlewares = (
  middlewares: Array<ResolverMiddleware<unknown, GraphQLContext>> = [],
  resolvers: ObjMap<Resolver>,
): ObjMap<Resolver> => {
  Object.keys(resolvers).forEach((k) => {
    resolvers[k] = resolvers[k].withMiddlewares(middlewares)
  })
  return resolvers
}