typescriptmergegraphqlresolvermercurius

Another way of merging graphql resolvers


In many tutorials that I have read, the most common way of using graphql resolvers is like below.

export const rootResolvers: IResolvers = {
  Query: {
    getUserById: async (root, { id }, context, info) => {
      return await getUserController(id);
    },
    getUsers: async (root, args, context, info) => {
      return await getUsersController();
    },
  },
  Mutation: {
    createUser: async (_, args, {userData}, info) => {
      return registerUserController(userData);
    },

    updateUser: async (_, args, {userData}, info) => {
      return updateUserController(userData);
    },
  },
}

But I want to write directly the busness logic in my resolvers without using controllers, and since each resolevr can have a lot of code, I decided to split my rootResolver like this.

const getUserResolver = {
   Query: {
    getUsers: async (root, args, context, info) => {
      return await getUsersController();
    },
    },
   }
}
...

const userCreateResolver = {
   Mutation: {
    createUser: async (_, args, {userData}, info) => {
            return registerUserController(userData);
    },
   }
}

const userUpdateResolver = {
   Mutation: {
    updateUser: async (_, args, {userData}, info) => {
            return registerUserController(userData);
    },
   }
}

Now I use the spread operator to combine all my resolvers in order to obtain one resolver object.

const rootResolver: IResolvers = {
  ...getUserResolver.Query,
  ...userCreateResolver.Mutation,
  ...userUpdateResolver.Mutation
}

I want to know if this way is the best practise. Note: I used lodash and graphql-tools/merge packages but got an error on the definition of graphql server ( I am using @mercuriusjs and the resolver must be an object not an array of object.

// Register mercurius to the server.
app.register(mercurius, {
  schema: buildFederationSchema(rootTypeDefs, {
    isGateway: false,
  }),
  resolvers: rootResolver,  <--------------------
  context: buildContext,
  graphiql: true,
  queryDepth: 7,
});


Solution

  • You can do this but don't include the Query or Mutation levels in your individual resolvers, use them at the end:

    const getUserResolver = {
      getUsers: getUsersController
    }
    
    const userCreateResolver = {
      createUser: (_, __, {userData}) =>  registerUserController(userData)
    }
    
    const userUpdateResolver = {
      updateUser: (_, __, {userData}) =>  registerUserController(userData)
    }
    

    You don't have to await resolvers in their code, resolvers can return promises.

    Then:

    const rootResolver: IResolvers = { 
      Query: {
        ...getUserResolver.Query,
      },
      Mutation: {
        ...userCreateResolver.Mutation,
        ...userUpdateResolver.Mutation
      }
    }