# Completing the Blog App - Backend

# Models

# Category Model

Defining the category model.

backend/src/models/Category.js

const mongoose = require("mongoose");

const categorySchema = new mongoose.Schema(
  {
    title: {
      type: String,
      required: true,
    },
    description: {
      type: String,
      required: true,
    },
    color: {
      type: String,
      required: true,
    },
  },
  { timestamps: true }
);

// Add a toJSON method to the schema to control the output of category instances
categorySchema.method("toJSON", function () {
  const { __v, _id, ...object } = this.toObject();
  object.id = _id;
  return object;
});

module.exports = mongoose.model("Category", categorySchema);

# Blog Model

Update the blog model.

backend/src/models/Blog.js

const mongoose = require("mongoose");

const blogSchema = new mongoose.Schema(
  {
    author: {
      type: Map,
      of: mongoose.Schema.Types.Mixed,
      required: true,
    },
    categoryIds: {
      type: [mongoose.Schema.Types.ObjectId],
      required: true,
      ref: "Category",
    },
    title: {
      type: String,
      required: true,
    },
    description: {
      type: String,
      required: true,
    },
    image: {
      type: String,
      default: "https://storage.googleapis.com/ix-blog-app/default.jpeg",
    },
    content: {
      type: Array,
      required: true,
    },
  },
  { timestamps: true }
);

// Add a toJSON method to the schema to control the output of blog instances
blogSchema.method("toJSON", function () {
  const { __v, _id, categoryIds, ...object } = this.toObject();
  object.id = _id;

  object.categories = categoryIds.map((category) => {
    return {
      id: category._id,
      title: category.title,
      description: category.description,
      color: category.color,
    };
  });

  // Ensure author is included in the returned object
  if (this.author) {
    object.author = this.author;
  }

  return object;
});

module.exports = mongoose.model("Blog", blogSchema);

# Controllers

# Blog Controller

Final blog controller.

backend/src/controllers/blogs.js

const Blog = require("../models/Blog");

const createBlogs = async (req, res) => {
  try {
    const categoryIds = req?.body?.categories.map((x) => x.id);
    const blog = new Blog({
      author: req?.body?.author,
      categoryIds: categoryIds,
      title: req?.body?.title,
      description: req?.body?.description,
      content: req?.body?.content,
    });
    const newBlog = await blog.save();
    const blogRes = await Blog.findById(newBlog._id).populate({
      path: "categoryIds",
    });
    res.status(201).json({ message: "New blog created!", data: blogRes });
  } catch (error) {
    res.status(500).json({ message: error.message, data: {} });
  }
};

const getBlogs = async (req, res) => {
  try {
    const blogs = await Blog.find().populate({ path: "categoryIds" });
    res.status(200).json({
      message: "Get all blogs!",
      data: blogs,
    });
  } catch (error) {
    res.status(500).json({ message: error.message, data: {} });
  }
};

const getBlogById = async (req, res) => {
  try {
    console.log(req.params.id);
    const blog = await Blog.findById(req.params.id).populate({
      path: "categoryIds",
    });
    if (blog) {
      res.status(200).json({ message: "Return blog by ID!", data: blog });
    } else {
      res.status(404).json({ message: "Blog not found!", data: {} });
    }
  } catch (error) {
    res.status(500).json({ message: error.message, data: {} });
  }
};

const getBlogsByCategoryID = async (req, res) => {
  try {
    console.log(req.params.id);
    let filter = {};
    if (req.params.id != "null" && req.params.id != "undefined") {
      filter = { categoryIds: req.params.id };
    }
    const blogs = await Blog.find(filter).populate({ path: "categoryIds" });
    res.status(200).json({
      message: "Get blogs by categoryID!",
      data: blogs,
    });
  } catch (error) {
    res.status(500).json({ message: error.message, data: {} });
  }
};

const updateBlogByID = async (req, res) => {
  console.log(req.body);
  try {
    const blog = await Blog.findById(req.params.id).populate({
      path: "categoryIds",
    });
    if (blog) {
      const categoryIds = req?.body?.categories.map((x) => x.id);
      blog.authorId = req?.body?.authorId || blog.authorId;
      blog.categoryIds = categoryIds ? categoryIds : blog.categoryIds;
      blog.title = req?.body?.title || blog.title;
      blog.description = req?.body?.description || blog.description;
      blog.content = req.body.content ? req.body.content : blog.content;
      const updatedBlog = await blog.save();
      const blogRes = await updatedBlog.populate({
        path: "categoryIds",
      });
      res.status(200).json({ message: "Blog updated!", data: blogRes });
    } else {
      res.status(404).json({ message: "Blog not found!", data: [] });
    }
  } catch (error) {
    res.status(500).json({ message: error.message, data: {} });
  }
};

const deleteBlogByID = async (req, res) => {
  try {
    const blog = await Blog.findByIdAndDelete(req.params.id);
    if (blog) {
      return res
        .status(200)
        .json({ message: "Blog deleted!", id: req.params.id });
    } else {
      return res.status(404).json({ message: "Blog not found!" });
    }
  } catch (error) {
    return res.status(500).json({ message: error.message });
  }
};

const blogController = {
  createBlogs,
  getBlogs,
  getBlogById,
  getBlogsByCategoryID,
  updateBlogByID,
  deleteBlogByID,
};

module.exports = blogController;

# Categories Controller

Final categories controller.

backend/src/controllers/categories.js

const Category = require("../models/Categories");

const createCategory = async (req, res) => {
  try {
    const category = new Category({
      title: req.body.title,
      description: req.body.description,
      color: req.body.color,
    });
    const newCategory = await category.save();
    const categoryRes = await Category.findById(newCategory._id);
    res
      .status(201)
      .json({ message: "New category created!", data: categoryRes });
  } catch (error) {
    res.status(500).json({ message: error.message, data: [] });
  }
};

const getCategories = async (req, res) => {
  try {
    const categories = await Category.find();
    res
      .status(200)
      .json({ message: "Return all categories!", data: categories });
  } catch (error) {
    res.status(500).json({ message: error.message, data: [] });
  }
};

const getCategoryById = async (req, res) => {
  console.log(req.params.id);
  res.status(200).json({
    message: "Get category by ID!",
    data: [],
  });
};

const updateCategoryByID = async (req, res) => {
  try {
    const category = await Category.findById(req.params.id);
    if (category) {
      category.title = req.body.title || category.title;
      category.description = req.body.description || category.description;
      category.color = req.body.color || category.color;
      const updatedCategory = await category.save();
      res
        .status(200)
        .json({ message: "Category updated!", data: updatedCategory });
    } else {
      res.status(404).json({ message: "Category not found!", data: [] });
    }
  } catch (error) {
    res.status(500).json({ message: error.message, data: [] });
  }
};

const deleteCategoryByID = async (req, res) => {
  try {
    const categoryDB = await Category.findById(req.params.id);
    if (!categoryDB) {
      res
        .status(400)
        .json({ message: "Cannot delete category with existing blogs!" });
    }
    const category = await Category.findByIdAndDelete(req.params.id);
    if (category) {
      res.status(200).json({ message: "Category deleted!", id: req.params.id });
    } else {
      res.status(404).json({ message: "Category not found!" });
    }
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
};

const categoryController = {
  createCategory,
  getCategories,
  getCategoryById,
  updateCategoryByID,
  deleteCategoryByID,
};

module.exports = categoryController;