CODEX // oaoisme wiki

§ unswayed-backend · Blog (Phase 8)

Blog — My Posts & free-flowing categories

updated 2026-06-29

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 + the meta pagination block, limit default 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 (in blog-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.