4.6 KiB
title, tags, aliases
| title | tags | aliases | |||||
|---|---|---|---|---|---|---|---|
| BlogPost |
|
|
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 definitionbackend/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.