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
- 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>
- Admin surface:
- full CRUD for posts/categories
- publish/unpublish actions
- relation payload updates (products/faqs/cta/videos)
- Public surface:
- paginated published posts
- published categories with counts
- slug detail with history fallback
| Method | Path | Permission |
|---|
GET | /api/admin/blog/posts | Blog_READ |
GET | /api/admin/blog/posts/:id | Blog_READ |
POST | /api/admin/blog/posts | Blog_CREATE |
PUT | /api/admin/blog/posts/:id | Blog_UPDATE |
DELETE | /api/admin/blog/posts/:id | Blog_DELETE |
POST | /api/admin/blog/posts/:id/publish | Blog_UPDATE |
POST | /api/admin/blog/posts/:id/unpublish | Blog_UPDATE |
| Method | Path | Permission |
|---|
GET | /api/admin/blog/categories | Blog_READ |
GET | /api/admin/blog/categories/:id | Blog_READ |
POST | /api/admin/blog/categories | Blog_CREATE |
PUT | /api/admin/blog/categories/:id | Blog_UPDATE |
DELETE | /api/admin/blog/categories/:id | Blog_DELETE |
| Method | Path | Auth |
|---|
GET | /api/blogs | Public |
GET | /api/blogs/categories | Public |
GET | /api/blogs/:slug | Public |
Mobile-composed equivalents:
GET /api/mobile/blogs
GET /api/mobile/blogs/categories
GET /api/mobile/blogs/:slug
| Param | Type | Default |
|---|
pagination | boolean | true |
page | number | 1 |
size | number | 20 |
sort | publishedAt | displayDate | createdAt | updatedAt | title | publishedAt |
order | asc | desc | desc |
search | string (min 2 chars) | optional |
categoryId | number | optional |
| Param | Type | Default |
|---|
pagination | boolean | true |
page | number | 1 |
size | number | 20 (max 100) |
sort | title | slug | status | displayDate | publishedAt | createdAt | updatedAt | updatedAt |
order | asc | desc | desc |
search | string (min 2 chars) | optional |
status | draft | published | optional |
categoryId | number | optional |
{
"title": "How to Read a Green Tara Thangka",
"slug": "how-to-read-green-tara-thangka",
"excerpt": "Symbolism and visual grammar explained",
"heroIntro": "Green Tara iconography decoded",
"body": "<p>...</p>",
"featuredImageUrl": "https://cdn.example.com/blog/green-tara.jpg",
"featuredImageAlt": "Green Tara detail",
"displayDate": "2026-05-01T00:00:00.000Z",
"categoryId": 1,
"seoId": "550e8400-e29b-41d4-a716-446655440000",
"status": "draft",
"productIds": [8, 3, 12],
"faqs": [
{
"question": "Is Green Tara always shown with one leg extended?",
"answer": "Often yes, representing readiness to act.",
"position": 0
}
],
"cta": {
"ctaTitle": "Explore Green Tara Thangkas",
"ctaButtonLabel": "Browse",
"ctaButtonLink": "https://nepathangka.com/products",
"position": 0
},
"videos": [
{
"title": "Green Tara Ritual Walkthrough",
"videoUrl": "https://cdn.example.com/videos/green-tara-ritual.mp4",
"seoId": "018f3c98-8dcf-7b1c-a8b9-c32b3f6de101",
"description": "Step-by-step visual guide",
"thumbnailUrl": "https://cdn.example.com/videos/green-tara-cover.jpg",
"position": 0
}
]
}
- Supports partial fields.
productIds: replace full linked-product set.
faqs: replace full FAQ set ([] clears all FAQs).
cta: object replaces CTA; null removes CTA.
videos: replace full video set ([] clears all videos).
{
"name": "Thangka Art Guides",
"description": "Educational writing on thangka iconography"
}
{
"message": "Blog posts fetched successfully",
"data": [
{
"id": 1,
"title": "How to Read a Green Tara Thangka",
"slug": "how-to-read-green-tara-thangka",
"excerpt": "Symbolism and visual grammar explained",
"featuredImageUrl": "https://cdn.example.com/blog/green-tara.jpg",
"featuredImageAlt": "Green Tara detail",
"displayDate": "2026-05-01T00:00:00.000Z",
"publishedAt": "2026-05-02T10:30:00.000Z",
"categoryName": "Thangka Art Guides",
"categorySlug": "thangka-art-guides"
}
],
"count": 42,
"currentPage": 1,
"totalPage": 3
}
{
"message": "Blog post fetched successfully",
"data": {
"id": 1,
"title": "How to Read a Green Tara Thangka",
"slug": "how-to-read-green-tara-thangka",
"excerpt": "...",
"heroIntro": "...",
"body": "<p>...</p>",
"featuredImageUrl": "https://cdn.example.com/blog/green-tara.jpg",
"featuredImageAlt": "Green Tara detail",
"displayDate": "2026-05-01T00:00:00.000Z",
"publishedAt": "2026-05-02T10:30:00.000Z",
"categoryName": "Thangka Art Guides",
"categorySlug": "thangka-art-guides",
"authorName": "Admin User",
"linkedProducts": [
{
"id": 8,
"title": "Sterling Silver Dorje Pendant",
"slug": "sterling-silver-dorje-pendant",
"mrp": 5400,
"sp": 4900,
"thumbnailUrl": "https://...",
"position": 0
}
],
"faqs": [
{
"question": "Is Green Tara always shown with one leg extended?",
"answer": "...",
"position": 0
}
],
"cta": {
"ctaTitle": "Explore Green Tara Thangkas",
"ctaButtonLabel": "Browse",
"ctaButtonLink": "https://nepathangka.com/products",
"position": 0
},
"videos": [
{
"id": 1,
"title": "Green Tara Ritual Walkthrough",
"videoUrl": "https://cdn.example.com/videos/green-tara-ritual.mp4",
"seoId": "018f3c98-8dcf-7b1c-a8b9-c32b3f6de101",
"description": "Step-by-step visual guide",
"thumbnailUrl": "https://cdn.example.com/videos/green-tara-cover.jpg",
"position": 0
}
],
"structuredData": {
"article": { "@type": "Article" },
"faq": { "@type": "FAQPage" },
"breadcrumb": { "@type": "BreadcrumbList" }
}
}
}
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
search requires at least 2 chars where defined.
size is globally capped at 100.
productIds deduped server-side and validated as published products.
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.
| errorCode | HTTP | Meaning |
|---|
BLOG_POST_NOT_FOUND | 404 | Post does not exist or not visible in current surface |
BLOG_CATEGORY_NOT_FOUND | 404 | Category not found |
BLOG_CATEGORY_NOT_EMPTY | 409 | Category has linked posts |
BLOG_CATEGORY_SLUG_ALREADY_EXISTS | 409 | Slug collision on category create/update |
BLOG_POST_INVALID_STATUS_TRANSITION | 400 | Publish/unpublish transition invalid |
PRODUCT_NOT_FOUND | 400 | One or more linked product IDs invalid/unpublished |
- 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.