Files
nick-doc/02 - Data Models/BlogPost.md
2026-05-23 20:35:34 +03:30

4.6 KiB

title, tags, aliases
title tags aliases
BlogPost
data-model
mongoose
Blog Post
Article
IBlogPost

BlogPost

Editorial content for the marketplace's blog. Each post has a title, an auto-generated slug, rich content, optional cover image, gallery, and embedded videos (YouTube / Vimeo / Aparat / other). Carries publication workflow (draft / published / archived), denormalised author info, SEO metadata, and counters for views, likes, and comments. Two pre-save hooks handle slug generation and publishedAt stamping.

[!note] Source backend/src/models/BlogPost.ts:39 — schema definition backend/src/models/BlogPost.ts:182 — model export

Schema

Field Type Required Default Validation Index Description
title String yes trim, maxlength 200 Post title.
slug String no (auto-generated) lowercase, trim unique, sparse URL slug.
description String yes maxlength 500 Short summary.
content String yes Full body (markdown / HTML).
coverImage String no Hero image URL.
images[] String[] no Gallery URLs.
videos[].url String yes Video URL.
videos[].title String no Video title.
videos[].platform String no youtube enum: youtube / vimeo / aparat / other Platform.
videos[].embedId String no Embed id (if applicable).
author.id ObjectId → User yes Author user.
author.name String yes Denormalised author name.
author.avatar String no Avatar URL.
category String yes tutorial enum: tutorial / news / guide / tips / announcement / other yes (compound) Editorial category.
tags[] String[] no trim yes Free-form tags.
status String no draft enum: draft / published / archived yes (compound) Workflow state.
publishedAt Date no yes (compound) Auto-set when status → published.
views Number no 0 View counter.
likes Number no 0 Like counter.
comments Number no 0 Comment counter.
readTime Number no 5 Estimated read time (minutes).
featured Boolean no false yes (compound) Front-page promotion.
seo.metaTitle String no SEO title.
seo.metaDescription String no SEO description.
seo.metaKeywords[] String[] no SEO keywords.
createdAt Date auto Mongoose timestamp.
updatedAt Date auto Mongoose timestamp.

Virtuals are enabled in toJSON and toObject even though none are declared on the schema.

Virtuals

None defined (but enabled in serialisation).

Indexes

Defined at backend/src/models/BlogPost.ts:148-151. Plus the implicit unique sparse index on slug:

  • { status: 1, publishedAt: -1 } — published feed.
  • { category: 1, status: 1 } — category page.
  • { tags: 1 } — tag lookup.
  • { featured: 1, status: 1 } — featured posts.

Pre/Post Hooks

Hook Behaviour
pre('save') (backend/src/models/BlogPost.ts:154) Auto-generates slug from the title (English letters only) plus a timestamp suffix; falls back to post-<timestamp> for non-Latin titles.
pre('save') (backend/src/models/BlogPost.ts:175) When status is modified to published and publishedAt is empty, sets publishedAt = new Date().

Instance Methods

None defined.

Static Methods

None defined.

Relationships

  • References: User (author.id).
  • Referenced by: none.

State Transitions

stateDiagram-v2
    [*] --> draft
    draft --> published : author publishes
    published --> archived : admin archives
    published --> draft : unpublish
    archived --> published : restore
    archived --> [*]

Common Queries

// Public feed
BlogPost.find({ status: 'published' }).sort({ publishedAt: -1 }).limit(20);

// By slug (detail page)
BlogPost.findOne({ slug, status: 'published' });

// Featured carousel
BlogPost.find({ featured: true, status: 'published' }).sort({ publishedAt: -1 });

// Tag search
BlogPost.find({ tags: tag, status: 'published' });

// Increment views atomically
BlogPost.updateOne({ _id }, { $inc: { views: 1 } });

Related: User.