Skip to content

Posts

Post Model

The Post model (replaces the API's Blog model) supports richer content management:

FieldAPI BlogCMS PostImprovement
Statuspublished (boolean) + inTrash (boolean)status (string: draft/published/scheduled/trash)Unified status field
SchedulingscheduledAt, publishedAtScheduled publishing
SEOmetaTitle, metaDescriptionSame + jsonLdSchemaStructured data support
URLcanonicalUrlcanonicalUrl + urlHistory[]Handles URL changes
Metadatameta (JSON: wordCount, readingTime, focusKeyphrase)Richer analytics
CategoriesblogCategoryId (single)blogCategoryId + subCategoryIdSubcategory support
Tagstags (string)tags (string)Same (stored as comma-separated)
GeographiccityId, country (string)cityId, countryIdProper Country model

Creating a Post

The CMS admin provides a form with:

  1. Title — Post title
  2. Slug — Auto-generated from title, manually editable
  3. Content — Lexical rich text editor
  4. Cover Image — Upload or URL
  5. Category — Post category (blog/travel-guide) + optional subcategory
  6. Author — Select from existing authors
  7. Tags — Comma-separated
  8. Status — Draft, Publish, or Schedule
  9. SEO — Meta title, meta description, JSON-LD schema
  10. Location — City, country for geographic tagging

Post Listing & Filtering

The CMS admin post list supports:

  • Status filter: All / Published / Draft / Scheduled / Trash
  • Search: By title
  • Sorting: By date, title
  • Bulk actions: Move to trash, restore, delete permanently

Categories & Subcategories

Posts have a two-level category hierarchy:

PostCategory (e.g., "Travel Guide", "Blog")
    └── SubCategory (e.g., "Trekking", "Culture", "Food")
  • A post must have a PostCategory (blog or travel-guide)
  • A post may optionally have a SubCategory
  • Categories determine the URL path: /{country}/{category.slug}/{post.slug}
  • Category "blog" maps to /blog/ routes; "travel-guide" maps to /travel-guide/ routes

Public API Endpoints

Single Post

GET /api/posts/{slug}?country=nepal&category=travel-guide

Returns the post with full content, author info, related posts (same category), and tag-based "You may also like" recommendations. Increments the view counter.

Published Posts

GET /api/posts/published?page=1&limit=20&search=trek&category=blog

Returns published posts with pagination. Supports search and category filtering. Used by the frontend for blog listings and sitemap generation.

Posts by Category

GET /api/posts/category/blog?page=1&limit=10
GET /api/posts/category/travel-guide?page=1&limit=10

Used by the frontend for blog and travel-guide listing pages.

Slug Resolution

GET /api/posts/resolve/{slug}?country=nepal&category=travel-guide

Returns { exists: true, canonicalPath, slug }. Also checks urlHistory for renamed posts. Used by the catch-all route to find posts.

URL Structure

The CMS generates canonical URLs using this logic:

1. Start with slug: /{post.slug}
2. If category exists, prepend: /{category.slug}/{post.slug}
3. If country exists, prepend: /{country.countryHandle}/{category.slug}/{post.slug}
4. Result: /nepal/travel-guide/best-time-to-visit

URL history is tracked — when a slug changes, the old URL is stored in urlHistory[] and the resolve endpoint still finds the post.

Built with VitePress