--- title: Blog API tags: [api, blog, reference] --- # Blog API Endpoints live under `/api/blog/*`. The router is [`backend/src/routes/blogRoutes.ts`](../../backend/src/routes/blogRoutes.ts) and delegates to [`blogController`](../../backend/src/services/blog/blogController.ts). Public endpoints (post listing and reads) are open. Mutating endpoints (create/update/delete) require **admin** role — enforced via `authenticateToken` + `authorizeRoles('admin')`. Model: [[BlogPost]]. Image uploads use the [[File API]] (`POST /api/files/upload/blog-images`). ## Public reads ### GET /api/blog/posts **Description:** Paginated list of published blog posts. **Auth required:** No **Query params:** - `page` (default 1) - `limit` (default 10) - `category` - `tag` - `search` (matches title/excerpt/content) - `sortBy` (default `publishedAt`) - `sortOrder` (`asc` | `desc`, default `desc`) **Response 200:** ```json { "success": true, "data": { "posts": [BlogPost, ...], "pagination": { "page": 1, "limit": 10, "total": 25, "totalPages": 3, "hasMore": true } } } ``` ### GET /api/blog/posts/featured **Description:** Posts with `isFeatured: true`. Used by the home page hero strip. **Auth required:** No **Response 200:** `{ success, data: { posts } }` ### GET /api/blog/posts/recent **Description:** Most recent N published posts. Used by the footer / sidebar. **Auth required:** No **Query params:** `limit` (default 5) ### GET /api/blog/posts/search **Description:** Full-text search across title/excerpt/content/tags. **Auth required:** No **Query params:** `q` (required), `page`, `limit` **Errors:** `400` if `q` is missing or shorter than 2 characters. ### GET /api/blog/posts/:slug **Description:** Get a single published post by URL slug. **Auth required:** No **Response 200:** `{ success, data: { post: BlogPost } }` **Errors:** `404` not found or not published. **Side effects:** Increments `viewsCount`. ## Admin (mutating) All endpoints below require `Bearer JWT` with `role: "admin"`. ### GET /api/blog/admin/posts **Description:** Admin list including drafts and unpublished posts. **Auth required:** Bearer JWT (admin) **Query params:** `page`, `limit`, `status` (`draft` | `published` | `archived`), `search` **Response 200:** `{ success, data: { posts, pagination } }` ### GET /api/blog/admin/posts/:id **Description:** Get a post by id (admin view, includes draft fields). **Auth required:** Bearer JWT (admin) ### POST /api/blog/posts **Description:** Create a post. **Auth required:** Bearer JWT (admin) **Request body:** ```ts { title: string; slug?: string; // auto-generated from title when omitted excerpt?: string; content: string; // Markdown / rich text coverImage?: string; // URL from [[File API]] category?: string; tags?: string[]; status?: "draft" | "published"; // default "draft" isFeatured?: boolean; publishedAt?: string; // ISO date, used when status === "published" videoUrl?: string; metadata?: Record; } ``` **Response 201:** `{ success, data: { post } }` ### PUT /api/blog/posts/:id **Description:** Update a post. Partial; the body can contain any of the create fields. **Auth required:** Bearer JWT (admin) ### DELETE /api/blog/posts/:id **Description:** Hard-delete a post. **Auth required:** Bearer JWT (admin) **Response 200:** `{ success: true, message: "Post deleted" }` ## Comments The codebase does not currently expose a comments API — comments are intentionally disabled. If/when added, they will appear under `/api/blog/posts/:id/comments`. ## Related - [[BlogPost]] - [[File API]] (for cover and inline image uploads) - `backend/BLOG_API_ENDPOINTS.md` (legacy in-repo notes) - `backend/BLOG_VIDEO_EXAMPLES.md`