Back-EndSchemas
Blog Schema
Complete MongoDB schemas for a blog website — Post, Category, and Comment with full relationships.
Blog Schema
Schemas for a blog platform — posts reference an author and category, comments reference a post. Built to work alongside the User Schema.
Category Schema
// src/models/category.model.js
import mongoose from "mongoose";
const categorySchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
unique: true,
trim: true,
},
slug: {
type: String,
required: true,
unique: true,
lowercase: true,
},
description: {
type: String,
default: "",
},
},
{ timestamps: true }
);
const Category = mongoose.model("Category", categorySchema);
export default Category;Post Schema
// src/models/post.model.js
import mongoose from "mongoose";
const postSchema = new mongoose.Schema(
{
title: {
type: String,
required: [true, "Title is required"],
trim: true,
maxlength: 150,
},
slug: {
type: String,
required: true,
unique: true,
lowercase: true,
},
excerpt: {
type: String,
maxlength: 300,
},
content: {
type: String,
required: [true, "Content is required"],
},
coverImage: {
type: String,
default: "",
},
author: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
category: {
type: mongoose.Schema.Types.ObjectId,
ref: "Category",
},
tags: [{ type: String, lowercase: true, trim: true }],
published: {
type: Boolean,
default: false,
},
publishedAt: {
type: Date,
},
views: {
type: Number,
default: 0,
},
likes: [{ type: mongoose.Schema.Types.ObjectId, ref: "User" }],
},
{ timestamps: true }
);
// auto-set publishedAt when a post is published for the first time
postSchema.pre("save", function (next) {
if (this.isModified("published") && this.published && !this.publishedAt) {
this.publishedAt = new Date();
}
next();
});
// index for fast text search on title and content
postSchema.index({ title: "text", content: "text" });
const Post = mongoose.model("Post", postSchema);
export default Post;Comment Schema
// src/models/comment.model.js
import mongoose from "mongoose";
const commentSchema = new mongoose.Schema(
{
post: {
type: mongoose.Schema.Types.ObjectId,
ref: "Post",
required: true,
},
author: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
content: {
type: String,
required: [true, "Comment cannot be empty"],
maxlength: 1000,
},
// supports nested replies — a reply references its parent comment
parentComment: {
type: mongoose.Schema.Types.ObjectId,
ref: "Comment",
default: null,
},
},
{ timestamps: true }
);
const Comment = mongoose.model("Comment", commentSchema);
export default Comment;How They Connect
// fetching a post with author and category populated
const post = await Post.findOne({ slug })
.populate("author", "name avatar")
.populate("category", "name slug");
// fetching comments for a post with author info
const comments = await Comment.find({ post: post._id, parentComment: null })
.populate("author", "name avatar")
.sort({ createdAt: -1 });Summary
PostreferencesUser(author) andCategory—tagsis a plain array of strings for lightweight taggingpublishedAtis set automatically the first time a post is published, using apre("save")hook- A text index on
titleandcontentenables$textsearch queries Commentsupports nested replies through a self-referencingparentCommentfield- Always use
.populate()to bring in author and category details rather than storing duplicate data