5.7 KiB
5.7 KiB
Blog CMS Backend API
Headless CMS API for content marketing across Lilith Platform brand domains.
Features
- Multi-domain blog posts - Single CMS serving atlilith.com, trustedmeet.com, etc.
- Content types - Blog posts, guides, tutorials, news, reports, case studies
- Rich taxonomy - Categories (hierarchical), tags, series
- SEO optimization - Meta tags, canonical URLs, Open Graph, robots directives
- RSS/Atom feeds - Syndication support for each domain
- Scheduled publishing - Cron-based auto-publishing
- View tracking - Basic analytics
- Soft deletes - Content recovery
Architecture
- Framework: NestJS 11 with SWC compiler
- Database: PostgreSQL (dedicated, port 25453)
- ORM: TypeORM with auto-load entities
- Validation: class-validator + class-transformer
- Scheduled tasks: @nestjs/schedule
- Health checks: @lilith/nestjs-health
Development
# Install dependencies
bun install
# Start dev server (watch mode)
bun run dev
# Build for production
bun run build
# Start production server
bun run start
# Type check
bun run typecheck
# Verify (circular dependency check)
bun run verify
# Seed database with Privacy Audit Report 2026
bun run seed
API Endpoints
Public Routes
Posts
GET /api/blog/posts- List posts (filterable, paginated)GET /api/blog/posts/:slug- Get single post by slug
Authors
GET /api/blog/authors- List all authorsGET /api/blog/authors/:slug- Get author with posts
Categories
GET /api/blog/categories?domain=- Get category treeGET /api/blog/categories/:slug- Get category with posts
Tags
GET /api/blog/tags- List tags with post countsGET /api/blog/tags/:slug- Get tag details
Series
GET /api/blog/series?domain=- List seriesGET /api/blog/series/:slug?domain=- Get series with ordered posts
Feeds
GET /api/blog/feed/rss?domain=- RSS 2.0 feedGET /api/blog/feed/atom?domain=- Atom feedGET /api/blog/feed/sitemap.xml?domain=- XML sitemap
Admin Routes (TODO: Add auth)
Posts
POST /api/blog/admin/posts- Create postPATCH /api/blog/admin/posts/:id- Update postDELETE /api/blog/admin/posts/:id- Soft delete postPOST /api/blog/admin/posts/:id/publish- Publish postPOST /api/blog/admin/posts/:id/unpublish- Unpublish postPOST /api/blog/admin/posts/:id/schedule- Schedule post
Authors, Categories, Tags, Series
- Standard CRUD at
/api/blog/admin/{resource}
Query Parameters
GET /api/blog/posts
{
domain?: string; // Filter by domain
contentType?: string; // blog-post, guide, tutorial, news, report, case-study
status?: string; // draft, published, scheduled, archived
categoryId?: string; // Filter by category UUID
categorySlug?: string; // Filter by category slug
tag?: string; // Filter by tag name
authorId?: string; // Filter by author UUID
authorSlug?: string; // Filter by author slug
seriesId?: string; // Filter by series UUID
seriesSlug?: string; // Filter by series slug
search?: string; // Search in title and content (ILIKE)
page?: number; // Page number (default: 1)
limit?: number; // Items per page (default: 20, max: 100)
sortBy?: string; // publishedAt, createdAt, updatedAt, views, title
sortOrder?: 'ASC' | 'DESC'; // Default: DESC
}
Response Format
{
data: Post[];
total: number;
page: number;
limit: number;
totalPages: number;
}
Database Schema
posts
- UUID primary key, slug (unique)
- title, content (text), excerpt
- contentType, status, domain
- Relations: author, category (nullable), series (nullable)
- seriesOrder (int, nullable)
- tags (text[] array)
- SEO: metaTitle, metaDescription, canonicalUrl, ogImage, robots
- publishedAt, scheduledFor (timestamptz)
- views (int), metadata (jsonb)
- Timestamps: createdAt, updatedAt, deletedAt (soft delete)
authors
- UUID primary key, slug (unique)
- name, bio, avatarUrl, website, twitterHandle
- userId (link to user system, nullable)
categories
- UUID primary key, slug (unique)
- name, description, parentId (self-referencing)
- sortOrder, domain
tags
- UUID primary key, slug (unique), name
series
- UUID primary key, slug (unique)
- title, description, domain, status
Scheduled Tasks
Publish Scheduler - Runs every minute
- Finds posts with
status='scheduled'andscheduledFor <= now - Publishes each post (sets status='published', publishedAt=now)
- Logs all operations
Seed Data
The seed command (bun run seed) populates the database with:
- Author: "Lilith Platform Research Team"
- Series: "Privacy Audit Report 2026"
- 11 Posts: Complete privacy audit report chapters
Data is sourced from codebase/features/landing/frontend-public/src/pages/compare/data/report-chapters/.
Environment Variables
DATABASE_POSTGRES_USER=blog
DATABASE_POSTGRES_PASSWORD=devpassword
DATABASE_POSTGRES_NAME=lilith_blog
DATABASE_HOST=localhost # Optional, default: localhost
DATABASE_PORT=25453 # Optional, default: 25453
NODE_ENV=development
Service Registry
The blog backend uses its own PostgreSQL instance on port 25453 (not shared with other features).
TODO
- Add authentication/authorization (admin routes)
- Add image upload service integration
- Add full-text search (PostgreSQL tsvector or Meilisearch)
- Add comment system
- Add revision history
- Add content approval workflow
- Add multi-language support (i18n)
- Add rate limiting
- Add caching layer (Redis)
- Add metrics/monitoring
- Add migration scripts for production
License
Private - Lilith Platform