Search code examples
mongodbmongoosegraphqlapolloapollo-server

Resolver must be an object or function (Graphql Relationship)


I'm trying to do a one to many relationship between category and products, but I get an error after I put my resolvers this is my code for reference.

ERROR SHOWS:enter image description here

Product Resolver:

 async getProductByCategory(mainCategory, {}) {
      try {
        const product = Product.find({ mainCategory: mainCategory._id });
        if (product) {
          return product;
        } else {
          throw new Error("Product Not Found");
        }
      } catch (err) {
        throw new Error(err);
      }
    },

MainCategory Resolver:

async getCategoryByProduct(product, args) {
      try {
        const mainCategory = MainCategory.findById(product._id);
        if (mainCategory) {
          return mainCategory;
        } else {
          throw new Error("Main Category Not Found");
        }
      } catch (err) {
        throw new Error(err);
      }
    },

Main Resolver:

const productsResolvers = require("./products");
const mainCategoriesResolvers = require("./mainCategories");


module.exports = {
    Product: {
      mainCategory: productsResolvers.getProductByCategory,
    },
    MainCategory: {
      products: mainCategoriesResolvers.getCategoryByProduct,
    },
    Query: {
      ...productsResolvers.Query,
      ...mainCategoriesResolvers.Query,
    },
    Mutation: {
      ...productsResolvers.Mutation,
      ...mainCategoriesResolvers.Mutation,
    },
   
  };

typeDefs:

type Product {
    id: ID! 
    mainCategory: MainCategory
  }

 type MainCategory {
    id: ID!
    products: [Product]
  }

Tell me if you need anymore code reference so that I can give you. Thanks in advance


Solution

  • Hi Guys I already solved it for those who want to know about one to many relationship in graphql, this is my code that I did.

    Product Model :

    const { model, Schema } = require("mongoose");
    
    const { ObjectId } = Schema.Types;
    
    const productSchema = new Schema({
      name: String,
      mainCategory: {
        type: ObjectId,
        ref: "MainCategory",
      },
    });
    
    module.exports = model("Product", productSchema);
    

    We want to make the mainCategory field or data an object only since we only want each product to have a category, the ref you see there should be exact as the model you named it which is here:

    module.exports = model("MainCategory", mainCategorySchema);
    

    For the MainCategory Model:

    const { model, Schema } = require("mongoose");
    
    const { ObjectId } = Schema.Types;
    
    const mainCategorySchema = new Schema({
      products: [
        {
          type: ObjectId,
          ref: "Product",
        },
      ],
    });
    
    module.exports = model("MainCategory", mainCategorySchema);
    

    Since you category right? category can have multiple products so we make it an array, then like the Product we should make the ref similar as we named the model.

    Now for the, TypeDefs:

    type Product {
        id: ID!
        mainCategory: MainCategory!
      }
    

    We make it only an MainCategory! since we want to return only an object.

    type MainCategory {
        id: ID!
        name: String!
        products: [Product!]
      }
    

    For the type MainCategory we make it like this [Product!] since we only want to return an array .

    now for the query in typedefs:

    type Query {
     getProducts: [Product]
     getMainCategories: [MainCategory]
    }
    

    this will get all the data of products and maincategories

    since we need to mutate the data or in other words create it we can do it like this:

    type Mutation {
        createProduct(
          name:String!
          mainCategory: String!
        ): Product!
         createMainCategory(
          name: String!
        ): MainCategory!
    }
    

    As you are asking right now? why mainCategory a string? why? because we are going to send an object id or the id of the category.

    As you noticed we didn't have a product createMainCategory since we are going to get that Id when we create a product which I will explain afterwards.

    Then we move onto the resolvers here:

    Product resolver:

    const Product = require("../../models/Product");
    
    module.exports = {
      Query: {
        async getProducts() {
          try {
            const products = await Product.find().sort({ createdAt: -1 });
            return products;
          } catch (err) {
            throw new Error(err);
          }
        },
      },
      Mutation: {
        async createProduct(_, { mainCategory }, context) {
          const newProduct = new Product({
            mainCategory,
          });
    
          const product = await newProduct.save();
    
          return product;
        },
      },
    };
    

    MainCategory resolver:

    const MainCategory = require("../../models/MainCategories");
    
    module.exports = {
      Query: {
        async getMainCategories() {
          try {
            const mainCategories = await MainCategory.find().sort({
              createdAt: -1,
            });
            return mainCategories;
          } catch (err) {
            throw new Error(err);
          }
        },
      },
      Mutation: {
        async createMainCategory(_, { name }, context) {
          const newMainCategory = new MainCategory({
            name,
          });
    
          const mainCategory = await newMainCategory.save();
    
          return mainCategory;
        },
      },
    };
    

    Then lastly on index.js:

    Product: {
        mainCategory: (parent) => 
        MainCategory.findById(parent.mainCategory),
    },
    
    MainCategory: {
        products: (parent) => Product.find({ mainCategory: parent._id 
    }),
    

    we define here we used the reason why I use parent so I dont get mixed up knowing if it is product or maincategory so in the product u have to define in which case we use mainCategory field, we use the MainCategory model take note and we use the parent which is the product to get the mainCategory field which is an object id and it will return to us the category with the same id

    for the mainCategory to get the array of products we use the Product model take note and find the same mainCategory and the parent id which is the mainCategory Id and it will return the array of products

    And here is your result: we first query Products

    enter image description here

    the result:

    enter image description here

    same applies to the mainCategory we first query getMainCategories

    enter image description here

    then the result:

    enter image description here

    Take note that I did the one to many relationship Please if you have any errors regarding this let me know, I am often active on stackoverflow but I mayble late will reply but I would reply right away when I see the notification.