Node.js, Apollo Server v4, GraphQL, Mongoose – Category Tree

Node.js, Apollo Server v4, GraphQL, Mongoose – Category Tree


1

I am trying to create Category Tree with Node.js, Apollo Server v4, GraphQL, Mongoose technologies.

But when I want to get the created Category or Subcategory data, the output is not what I want. Categories appear both in subCategories as a reference and as a normal category. I also added category data with this id 655b105543598f2ef0ba0f69 as the 2nd subCategory in the subCategories array of the category with this id 655b104a43598f2ef0ba0f64 value in a way I don’t understand. But although the name does not appear null.

GraphQL Query Request;

query Query {
  getCategories {
    success
    response_code
    message
    categories {
      _id
      name
      attributes {
        name
        values {
          value
        }
      }
      products {
        name
      }
      subCategories {
        _id
        name
        attributes {
          name
          values {
            value
          }
        }
        products {
          name
        }
        subCategories {
          _id
          name
          attributes {
            name
            values {
              value
            }
          }
          products {
            name
          }
          subCategories {
            _id
            name
            attributes {
              name
              values {
                value
              }
            }
            products {
              name
            }
            subCategories {
              _id
              name
              attributes {
                name
                values {
                  value
                }
              }
              products {
                name
              }
            }
          }
        }
      }
    }
  }
}

Output;

{
  "data": {
    "getCategories": {
      "success": true,
      "response_code": "categories-successfully-retrieved",
      "message": "Categories Successfully Retrieved!",
      "categories": [
        {
          "_id": "655b103b43598f2ef0ba0f5e",
          "name": "K 2",
          "attributes": {
            "name": null,
            "values": {
              "value": null
            }
          },
          "products": [],
          "subCategories": []
        },
        {
          "_id": "655b103e43598f2ef0ba0f61",
          "name": "K 1",
          "attributes": {
            "name": null,
            "values": {
              "value": null
            }
          },
          "products": [],
          "subCategories": [
            {
              "_id": "655b104a43598f2ef0ba0f64",
              "name": "AK 1",
              "attributes": {
                "name": null,
                "values": {
                  "value": null
                }
              },
              "products": [],
              "subCategories": [
                {
                  "_id": "655b105543598f2ef0ba0f69",
                  "name": null,
                  "attributes": null,
                  "products": null,
                  "subCategories": null
                }
              ]
            },
            {
              "_id": "655b106643598f2ef0ba0f6f",
              "name": "AK 2",
              "attributes": {
                "name": null,
                "values": {
                  "value": null
                }
              },
              "products": [],
              "subCategories": []
            }
          ]
        },
        {
          "_id": "655b104a43598f2ef0ba0f64",
          "name": "AK 1",
          "attributes": {
            "name": null,
            "values": {
              "value": null
            }
          },
          "products": [],
          "subCategories": [
            {
              "_id": "655b105543598f2ef0ba0f69",
              "name": "AAK 1",
              "attributes": {
                "name": null,
                "values": {
                  "value": null
                }
              },
              "products": [],
              "subCategories": []
            }
          ]
        },
        {
          "_id": "655b105543598f2ef0ba0f69",
          "name": "AAK 1",
          "attributes": {
            "name": null,
            "values": {
              "value": null
            }
          },
          "products": [],
          "subCategories": []
        },
        {
          "_id": "655b106643598f2ef0ba0f6f",
          "name": "AK 2",
          "attributes": {
            "name": null,
            "values": {
              "value": null
            }
          },
          "products": [],
          "subCategories": []
        }
      ]
    }
  }
}

Expected Output;

[
  {
  "name": "Category 1",
  "attributes": {
    "name": "Attribute Name",
    "values": [{
      "value": "Attribute Value",
      "products": ["Product Ref", "Product Ref"]
    }]
  },
  "products": ["Product Ref", "Product Ref"],
  "subCategories": [
      {
      "name": "Sub Category 1",
      "attributes": {
        "name": "Attribute Name",
        "values": [{
          "value": "Attribute Value",
          "products": ["Product Ref", "Product Ref"]
        }]
      },
      "products": ["Product Ref", "Product Ref"],
      "subCategories": [
        {
          "name": "Sub Category 2",
          "attributes": {
            "name": "Attribute Name",
            "values": [{
              "value": "Attribute Value",
              "products": ["Product Ref", "Product Ref"]
            }]
          },
          "products": ["Product Ref", "Product Ref"],
          "subCategories": []
        }
      ]
    }
  ]
},
{
  "name": "Category 2",
  "attributes": {
    "name": "Attribute Name",
    "values": [{
      "value": "Attribute Value",
      "products": ["Product Ref", "Product Ref"]
    }]
  },
  "products": ["Product Ref", "Product Ref"],
  "subCategories": [
      {
      "name": "Sub Category 1",
      "attributes": {
        "name": "Attribute Name",
        "values": [{
          "value": "Attribute Value",
          "products": ["Product Ref", "Product Ref"]
        }]
      },
      "products": ["Product Ref", "Product Ref"],
      "subCategories": [
        {
          "name": "Sub Category 2",
          "attributes": {
            "name": "Attribute Name",
            "values": [{
              "value": "Attribute Value",
              "products": ["Product Ref", "Product Ref"]
            }]
          },
          "products": ["Product Ref", "Product Ref"],
          "subCategories": []
        }
      ]
    }
  ]
}
]

Category Mongoose Schema;

import mongoose from "mongoose";

const attributeSchema = new mongoose.Schema({
  name: { type: String, required: true, trim: true, unique: true, default: "Default Name" },
  values: [
    {
      value: { type: String, required: true, trim: true, unique: true },
      products: [{ type: mongoose.Schema.Types.ObjectId, ref: "Product" }],
    },
  ],
});

const categorySchema = new mongoose.Schema({
  name: { type: String, required: true, trim: true },
  subCategories: [{ type: mongoose.Schema.Types.ObjectId, ref: "Category" }],
  attributes: [attributeSchema],
  products: [{ type: mongoose.Schema.Types.ObjectId, ref: "Product" }],
});

export const Category = mongoose.model("Category", categorySchema);

Category GraphQL Schema;

export const categorySchema = `#graphql
  type Category {
    _id: ID
    name: String
    subCategories: [Category]
    attributes: Attributes
    products: [Product]
  }

  input CategoryInput {
    name: String!
    isMainCategory: Boolean!
    parentCategoryID: ID
  }

  type Attributes {
    name: String
    values: AttributesValue
  }

  type AttributesValue {
    value: String
    products: [Product]
  }

  input AttributesInput {
    name: String!
    categoryID: ID!
  }

  input AttributesValueInput {
    value: String!
    attributesID: ID!
  }

  type QueryCategoryResponse {
    success: Boolean!
    response_code: String!
    message: String!
    category: Category
    categories: [Category]
  }

  type Query {
    getCategories: QueryCategoryResponse
    getCategoryByID(id: ID!): QueryCategoryResponse
    getCategoriesByID(ids: [ID]!): QueryCategoryResponse
  }

  type Mutation {
    createCategory(input: CategoryInput!): Response
  }
`;

Category Resolver;

import { Category } from "./model.js";

export const categoryResolver = {
  Query: {
    getCategories: async () => {
      try {
        const categories = await Category.find().populate("subCategories attributes products");

        if (categories) {
          return {
            success: true,
            response_code: "categories-successfully-retrieved",
            message: "Categories Successfully Retrieved!",
            categories: categories,
          };
        } else {
          return { success: false, response_code: "categories-not-found", message: "Categories Not Found!" };
        }
      } catch (error) {
        return { success: false, response_code: "server-error", message: "Server Error!" };
      }
    },
    getCategoryByID: async (_, { id }) => {
      try {
        const category = await Category.findById({ _id: id }).populate("subCategories attributes products");

        if (category) {
          return {
            success: true,
            response_code: "category-successfully-retrieved",
            message: "Category Successfully Retrieved!",
            category: category,
          };
        } else {
          return { success: false, response_code: "category-not-found", message: "Category Not Found!" };
        }
      } catch (error) {
        return { success: false, response_code: "server-error", message: "Server Error!" };
      }
    },
    getCategoriesByID: async (_, { ids }) => {
      try {
        const categories = await Category.find({ _id: { $in: ids } }).populate("subCategories attributes products");

        if (categories.length !== 0) {
          return {
            success: true,
            response_code: "categories-successfully-retrieved",
            message: "Categories Successfully Retrieved!",
            categories: categories,
          };
        } else {
          return { success: false, response_code: "categories-not-found", message: "Categories Not Found!" };
        }
      } catch (error) {
        return { success: false, response_code: "server-error", message: "Server Error!" };
      }
    },
  },
  Mutation: {
    createCategory: async (_, { input }) => {
      try {
        if (input.isMainCategory) {
          const category = await Category.findOne({ name: input.name }).collation({ locale: "en", strength: 2 });

          if (category) {
            return { success: false, response_code: "category-name-exist", message: "Category Name Exist!" };
          } else {
            const newCategory = new Category({
              name: input.name,
            });

            await newCategory.save();

            return {
              success: true,
              response_code: "category-created-successfully",
              message: "Category Created Successfully!",
            };
          }
        } else {
          const category = await Category.findById(input.parentCategoryID).populate("subCategories");

          if (!category) {
            return { success: false, response_code: "sub-category-not-found", message: "Sub Category Not Found!" };
          }

          const categoryNames = category.subCategories.map((category) => category.name.toLowerCase());

          if (categoryNames.includes(input.name.toLowerCase())) {
            return { success: false, response_code: "category-name-exist", message: "Category Name Exists!" };
          }

          const newCategory = new Category({
            name: input.name,
          });

          await newCategory.save();

          category.subCategories.push(newCategory);
          await category.save();

          return {
            success: true,
            response_code: "sub-category-created-successfully",
            message: "Sub Category Created Successfully!",
          };
        }
      } catch (error) {
        console.log(error);
        return { success: false, response_code: "server-error", message: "Server Error!" };
      }
    },
  },
};

How can i improve this code and solve my getCategories output problem?

Obviously, I also tried with the mongoose ref, I also tried to do it by sending data directly into subCategories in accordance with the categorySchema. The method I mentioned in the 2nd method on adding subcategories etc. was a headache for me.

New contributor

Knâkles is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

2

  • 1

    Can you tell us what output you are looking for?

    – Michel Floyd

    12 hours ago

  • Something like this hastebin.com/share/ipexinaruj.json @MichelFloyd

    – Knâkles

    2 hours ago



Load 7 more related questions


Show fewer related questions

0



Leave a Reply

Your email address will not be published. Required fields are marked *