Happy House - Ecommerce Docs
Developer ResourcesBlog

Blog Module API & Integration Guide

Admin and public blog API contracts, payload rules, and integration behavior.

Audience: Frontend and integration developers Scope: Blog admin CRUD + public blog consumption

Blog Module - API & Integration Guide

1. Quick Metadata

  • Module: Blog
  • Admin base URLs:
    • /api/admin/blog/posts
    • /api/admin/blog/categories
  • Public base URL: /api/blogs
  • Mobile-composed public base URL: /api/mobile/blogs
  • Response envelope: ResponseDto<T>

2. High-Level API Split

  • Admin surface:
    • full CRUD for posts/categories
    • publish/unpublish actions
    • relation payload updates (faqs/cta/videos)
  • Public surface:
    • paginated published posts
    • published categories with counts
    • slug detail with history fallback

3. Route Summary

3.1 Admin Posts

MethodPathPermission
GET/api/admin/blog/postsBlog_READ
GET/api/admin/blog/posts/:idBlog_READ
POST/api/admin/blog/postsBlog_CREATE
PUT/api/admin/blog/posts/:idBlog_UPDATE
DELETE/api/admin/blog/posts/:idBlog_DELETE
POST/api/admin/blog/posts/:id/publishBlog_UPDATE
POST/api/admin/blog/posts/:id/unpublishBlog_UPDATE

3.2 Admin Categories

MethodPathPermission
GET/api/admin/blog/categoriesBlog_READ
GET/api/admin/blog/categories/:idBlog_READ
POST/api/admin/blog/categoriesBlog_CREATE
PUT/api/admin/blog/categories/:idBlog_UPDATE
DELETE/api/admin/blog/categories/:idBlog_DELETE

3.3 Public Blog

MethodPathAuth
GET/api/blogsPublic
GET/api/blogs/categoriesPublic
GET/api/blogs/:slugPublic

Mobile-composed equivalents:

  • GET /api/mobile/blogs
  • GET /api/mobile/blogs/categories
  • GET /api/mobile/blogs/:slug

4. Query Parameters

4.1 Public list (GET /api/blogs)

ParamTypeDefault
paginationbooleantrue
pagenumber1
sizenumber20
sortpublishedAt | displayDate | createdAt | updatedAt | titlepublishedAt
orderasc | descdesc
searchstring (min 2 chars)optional
categoryIdnumberoptional

4.2 Admin list (GET /api/admin/blog/posts)

ParamTypeDefault
paginationbooleantrue
pagenumber1
sizenumber20 (max 100)
sorttitle | slug | status | displayDate | publishedAt | createdAt | updatedAtupdatedAt
orderasc | descdesc
searchstring (min 2 chars)optional
statusdraft | publishedoptional
categoryIdnumberoptional

5. Request Payload Contracts

5.1 Admin create post (POST /api/admin/blog/posts)

{
  "title": "The Ultimate Guide to Choosing a Sofa",
  "slug": "ultimate-guide-to-choosing-a-sofa",
  "excerpt": "Sofa sizing, frame, and fabric explained",
  "heroIntro": "How to pick a sofa that fits your space",
  "body": "<p>...</p>",
  "featuredImageUrl": "https://cdn.example.com/blog/sofa-showroom.jpg",
  "featuredImageAlt": "Grey linen sofa detail",
  "displayDate": "2026-05-01T00:00:00.000Z",
  "categoryId": 1,
  "seoId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "draft",
  "faqs": [
    {
      "question": "What sofa size fits a small living room?",
      "answer": "A two-seater or compact three-seater under 200cm wide usually works best.",
      "position": 0
    }
  ],
  "cta": {
    "ctaTitle": "Browse Our Sofa Collection",
    "ctaButtonLabel": "Shop Sofas",
    "ctaButtonLink": "https://www.happyhouse.example/sofas",
    "position": 0
  },
  "videos": [
    {
      "title": "Sofa Fabric Guide Walkthrough",
      "videoUrl": "https://cdn.example.com/videos/sofa-fabric-guide.mp4",
      "seoId": "018f3c98-8dcf-7b1c-a8b9-c32b3f6de101",
      "description": "Step-by-step fabric comparison guide",
      "thumbnailUrl": "https://cdn.example.com/videos/sofa-fabric-cover.jpg",
      "position": 0
    }
  ]
}

5.2 Admin update post (PUT /api/admin/blog/posts/:id)

  • Supports partial fields.
  • faqs: replace full FAQ set ([] clears all FAQs).
  • cta: object replaces CTA; null removes CTA.
  • videos: replace full video set ([] clears all videos).

5.3 Admin create/update category

{
  "name": "Living Room Furniture",
  "description": "Buying guides and styling tips for living room furniture"
}

6. Response Shape Examples

6.1 Public list success

{
  "message": "Blog posts fetched successfully",
  "data": [
    {
      "id": 1,
      "title": "The Ultimate Guide to Choosing a Sofa",
      "slug": "ultimate-guide-to-choosing-a-sofa",
      "excerpt": "Sofa sizing, frame, and fabric explained",
      "featuredImageUrl": "https://cdn.example.com/blog/sofa-showroom.jpg",
      "featuredImageAlt": "Grey linen sofa detail",
      "displayDate": "2026-05-01T00:00:00.000Z",
      "publishedAt": "2026-05-02T10:30:00.000Z",
      "categoryName": "Living Room Furniture",
      "categorySlug": "living-room-furniture"
    }
  ],
  "count": 42,
  "currentPage": 1,
  "totalPage": 3
}

6.2 Public detail success

{
  "message": "Blog post fetched successfully",
  "data": {
    "id": 1,
    "title": "The Ultimate Guide to Choosing a Sofa",
    "slug": "ultimate-guide-to-choosing-a-sofa",
    "excerpt": "...",
    "heroIntro": "...",
    "body": "<p>...</p>",
    "featuredImageUrl": "https://cdn.example.com/blog/sofa-showroom.jpg",
    "featuredImageAlt": "Grey linen sofa detail",
    "displayDate": "2026-05-01T00:00:00.000Z",
    "publishedAt": "2026-05-02T10:30:00.000Z",
    "categoryName": "Living Room Furniture",
    "categorySlug": "living-room-furniture",
    "authorName": "Admin User",
    "faqs": [
      {
        "question": "What sofa size fits a small living room?",
        "answer": "...",
        "position": 0
      }
    ],
    "cta": {
      "ctaTitle": "Browse Our Sofa Collection",
      "ctaButtonLabel": "Shop Sofas",
      "ctaButtonLink": "https://www.happyhouse.example/sofas",
      "position": 0
    },
    "videos": [
      {
        "id": 1,
        "title": "Sofa Fabric Guide Walkthrough",
        "videoUrl": "https://cdn.example.com/videos/sofa-fabric-guide.mp4",
        "seoId": "018f3c98-8dcf-7b1c-a8b9-c32b3f6de101",
        "description": "Step-by-step fabric comparison guide",
        "thumbnailUrl": "https://cdn.example.com/videos/sofa-fabric-cover.jpg",
        "position": 0
      }
    ],
    "structuredData": {
      "article": { "@type": "Article" },
      "faq": { "@type": "FAQPage" },
      "breadcrumb": { "@type": "BreadcrumbList" }
    }
  }
}

6.3 Slug fallback behavior

If request is GET /api/blogs/old-slug-name and old slug exists in blog_slug_history for a published post:

  • returns 200 with current post data
  • response data.slug is current canonical slug
  • no redirect status/code is returned

7. Validation and Constraints

  • search requires at least 2 chars where defined.
  • size is globally capped at 100.
  • videos is capped at 20 entries.
  • Every video requires a valid seoId and each seoId must be unique within payload.
  • Video seoId cannot already be linked to another blog post video.
  • Category delete returns conflict when category is not empty.

8. Error Reference

errorCodeHTTPMeaning
BLOG_POST_NOT_FOUND404Post does not exist or not visible in current surface
BLOG_CATEGORY_NOT_FOUND404Category not found
BLOG_CATEGORY_NOT_EMPTY409Category has linked posts
BLOG_CATEGORY_SLUG_ALREADY_EXISTS409Slug collision on category create/update
BLOG_POST_INVALID_STATUS_TRANSITION400Publish/unpublish transition invalid

9. Integration Notes

  • For public detail pages, frontend should consume structuredData and render JSON-LD tags.
  • For historical links, frontend should not expect redirects; use returned canonical slug if URL normalization is needed.
  • Public endpoints are available identically under /api/mobile/blogs*.
  • After admin mutation success, frontend can safely refetch; cache invalidation happens server-side.

See Also