§ unswayed-backend · Blog (Phase 8)
Blog — My Posts & free-flowing categories
Two small additions to the blog module for the employer dashboard. No schema
change, no migration — both live in src/blog/blogs-read.{controller,service}.ts.
GET /api/blogs/mine — the caller's own posts
The public GET /api/blogs returns everyone's blogs. The employer dashboard's
"My Posts" view needs only the signed-in user's. Rather than overload the public
list with an auth-conditional ?user_id=me, this is a dedicated route —
clearer, and it leaves the public list's behaviour untouched.
- Auth:
JwtAuthGuard + AccountStatusGuard(bearer). - Scope:
Blog.userId === caller.id. - Shape: identical to
GET /api/blogs— count-less cards + themetapagination block,limitdefault 10 (clamped 1..50),page≥ 1. So the dashboard reuses the same rendering code; pagination is preserved.
The route-ordering gotcha
@Get('blogs/mine') must be declared before @Get('blogs/:id'). NestJS
matches routes in declaration order, so if blogs/:id came first it would
capture mine as an :id (and the detail handler would 404 on the non-numeric
"mine"). Static segments before parameterised ones — a small but classic trap.
GET /api/blog-categories — free-flowing, not a hard enum
The frontend has a predefined category list and wanted the backend to be the source of truth. The instinct is a strict enum that rejects anything else — but that's brittle: it breaks the moment someone wants a new category, and it would reject the free-text values already stored.
So category stays free-text (String?, validated only for length). The
endpoint returns a living list:
categories = unique( SUGGESTIONS ∪ distinct(Blog.category where not null) ) // sorted
SUGGESTIONS(inblog-categories.ts) is a seed of sensible defaults so the list is never empty.- The distinct in-use values mean a brand-new category, the moment it's used on a post, automatically appears in the list next time — "fixed list, but new ones get added."
This is the general lesson: when a "category" or "tag" needs to be both suggestable and open-ended, derive the list from the data (+ a seed) instead of hard-coding an enum. You get suggestions for the UI without locking the door.
See memory entry 0038 for the file map.